配置OAuth2.0
代码结构
1、依赖准备
<!--spring security的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--OAuth2的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
2、编写配置类
1、令牌配置类
package team.lcf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* @program: LuoThinking-cloud
* @description: 令牌相关配置类
* @author: Cheng Zhi
* @create: 2022-05-25 18:29
**/
@Configuration
public class AccessTokenConfig {
/**
* todo 暂时写死,后续需要配置到文件中
*/
private String SINGING_KEY = "czz";
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 设置秘钥
// todo 后面改成非对称加密
converter.setSigningKey(SINGING_KEY);
System.out.println("--------" + SINGING_KEY);
return converter;
}
}
2、认证服务器配置(注意这里如果指定数据库模式,需要依赖spring-boot-starter-jdbc)
package team.lcf.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import javax.sql.DataSource;
/**
* @program: LuoThinking-cloud
* @description: 授权服务
* @author: Cheng Zhi
* @create: 2022-05-25 18:06
**/
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 密码模式需要的认证管理器
*/
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
public AuthorizationCodeServices authorizationCodeServices;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private DataSource dataSource;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()") // /oauth/token_key 是公开的
.checkTokenAccess("permitAll()") // /oauth/check_token公开
.allowFormAuthenticationForClients(); // 表单认证
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
/* // todo 暂时写死,之后要放到数据库
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("authorization_code", "implicit", "client_credentials", "password", "refresh_token")
// 过期时间
//.accessTokenValiditySeconds(1800)
.resourceIds("rid")
.scopes("all")
.secret(new BCryptPasswordEncoder().encode("123"));*/
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager) // 认证管理器
.userDetailsService(userDetailsService) // 用户信息
.authorizationCodeServices(authorizationCodeServices) // 授权码管理器
.tokenServices(tokenServices()) // 令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
.pathMapping("/oauth/token","/login"); // 这里自定义获取令牌路径
}
/**
* 令牌管理服务的配置
*/
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
//客户端端配置策略
services.setClientDetailsService(clientDetailsService);
//支持令牌的刷新
services.setSupportRefreshToken(true);
//令牌服务
services.setTokenStore(tokenStore);
//access_token的过期时间
services.setAccessTokenValiditySeconds(60 * 60 * 2);
//refresh_token的过期时间
services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
//设置令牌增强,使用JwtAccessTokenConverter进行转换
services.setTokenEnhancer(jwtAccessTokenConverter);
return services;
}
/**
* todo 暂时设置授权码模式内存模式
* @return
*/
/*
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
*/
/**
* 授权码模式保存到数据库
* @param dataSource
* @return
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
ClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
((JdbcClientDetailsService) jdbcClientDetailsService).setPasswordEncoder(passwordEncoder);
return jdbcClientDetailsService;
}
}
3、配置SpringSecurity管理器
package team.lcf.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import team.lcf.filter.LoginFilter;
import team.lcf.pub.RespBean;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @program: LuoThinking-cloud
* @description: Security安全配置
* @author: Cheng Zhi
* @create: 2022-05-25 10:11
**/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/* @Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}*/
/**
* 配置加密规则
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 配置显示用户找不到异常,如果不配置默认为true,为ture表示隐藏错误信息,展示BadCredentialsException异常
* @return
*/
@Bean
public Boolean hideUserNotFoundExceptions() {
return false;
}
/* @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO 这里暂时写死,之后转移到数据库
auth.inMemoryAuthentication()
.withUser("admin")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("admin")
.and()
.withUser("user")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("user");
super.configure(auth);
}*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO 这里之后要重新配置,参照FrameUser
http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/**")
.permitAll()
.and().csrf().disable();
}
}
4、配置用户信息查询
package team.lcf.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import team.lcf.enums.ErrorCode;
import team.lcf.exception.GeneralException;
import team.lcf.mapper.FrameUserMapper;
import team.lcf.model.FrameUser;
import team.lcf.model.Role;
import team.lcf.pub.BeanHelper;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* @program: PostGirl-panent
* @description: loadUserByUserNameServiceImpl
* @author: Cheng Zhi
* @create: 2021-03-30 12:49
**/
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
private BeanHelper beanHelper;
/**
* 根据用户名查询用户信息,用于登录
* @param userName
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
FrameUser user = beanHelper.getBean(FrameUserMapper.class).loadUserByUsername(userName);
if (user == null) {
throw new GeneralException(ErrorCode.FILE_ANALYSE_ERROR);
}
Integer userId = user.getUserId();
List<Role> roles = beanHelper.getBean(FrameUserMapper.class).selectRoleByUserId(userId);
user.setRoles(roles);
return (UserDetails) user;
}
}
效果
注意:
认证服务如果选择数据库模式,需要配置固定的几张表
官网地址: https://github.com/spring-projects/spring-security-oauth/blob/main/spring-security-oauth2/src/test/resources/schema.sql
我这里记录一下我自己的sql:
CREATE TABLE oauth_client_details(
client_id VARCHAR(128) NOT NULL COMMENT '客户端ID' ,
resource_ids VARCHAR(128) COMMENT '用户ID 客户端额能访问的ID资源集合,多个使用逗号隔开' ,
client_secret VARCHAR(128) COMMENT '客户端密码 客户端的访问密钥' ,
scope VARCHAR(128) COMMENT '范围 客户端的可选权限范围,read,write,trust,多个权限范围使用逗号隔开' ,
authorized_grant_types VARCHAR(128) COMMENT '授权类型' ,
web_server_redirect_uri VARCHAR(128) COMMENT '重定向url' ,
authorities VARCHAR(128) COMMENT '当前' ,
access_token_validity BIGINT COMMENT 'token令牌有效性' ,
refresh_token_validity BIGINT COMMENT 'token刷新' ,
additional_information VARCHAR(512) COMMENT '附加信息' ,
autoapprove VARCHAR(32) COMMENT '自动续约' ,
ext1 VARCHAR(32) COMMENT 'ext1' ,
PRIMARY KEY (client_id)
) COMMENT = '客户端明细信息 保存 Oauth2客户端账号密码、授权、回调地址等';
insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`, `ext1`) values('password','rid','$2a$10$r9gQOnCC77KBiHUGY0RZh.39ZPJs3lfgF6bMY7wksXzywWO6rX44O','ALL','authorization_code,implicit,client_credentials,password,refresh_token',NULL,NULL,NULL,NULL,NULL,NULL,NULL);
CREATE TABLE oauth_code(
code INT COMMENT '验证码' ,
authentication VARCHAR(128) COMMENT '身份验证' ,
ext1 VARCHAR(32) COMMENT 'ext1'
) COMMENT = '授权码 存储授权码';
版权声明:本文为cz1803472613原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。