OAuth 2.0是一种开放的授权协议,它允许用户授权第三方应用访问其账户(或资源),而无需共享其用户账户凭据。在Spring Boot中,我们可以使用Spring Security的OAuth2.0模块来实现授权验证。
依赖引入
配置认证服务器
/**
* @author Charles
* @module springboot
* @since 2023/6/19 16:30
*/
@Configuration
@EnableAuthorizationServer //开启认证服务器
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//在 MyOAuth2Config 添加到容器了
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
@Qualifier("jwtTokenStore")
TokenStore jwtTokenStore;
@Autowired
JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
TokenEnhancerChain tokenEnhancerChain;
/**
* 配置被允许访问此认证服务器的客户端详细信息
* 1.内存管理
* 2.数据库管理方式
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//客户端名称
.withClient("test-pc")
//客户端密码
.secret(passwordEncoder.encode("123456"))
//资源id,商品资源
.resourceIds("oauth2-server")
//授权类型, 可同时支持多种授权类型
.authorizedGrantTypes("authorization_code", "password", "implicit","client_credentials","refresh_token")
//授权范围标识,哪部分资源可访问(all是标识,不是代表所有)
.scopes("all")
//false 跳转到授权页面手动点击授权,true 不用手动授权,直接响应授权码
.autoApprove(false)
//客户端回调地址
.redirectUris("http://www.baidu.com/")
;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//密码模式需要配置认证管理器
endpoints.authenticationManager(authenticationManager);
//刷新令牌获取新令牌时需要
endpoints.userDetailsService(myUserDetailsService);
endpoints.tokenEnhancer(tokenEnhancerChain);
//令牌管理策略
// endpoints.tokenStore(tokenStore);
//设置为jwt存储
endpoints.tokenStore(jwtTokenStore);
endpoints.accessTokenConverter(jwtAccessTokenConverter);
//授权码管理策略,针对授权码模式有效,会将授权码放到 auth_code 表,授权后就会删除它
//endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices);
DefaultTokenServices tokenService = getTokenStore(endpoints);
endpoints.tokenServices(tokenService);
}
//配置TokenService参数
private DefaultTokenServices getTokenStore(AuthorizationServerEndpointsConfigurer endpoints) {
DefaultTokenServices tokenService = new DefaultTokenServices();
tokenService.setTokenStore(endpoints.getTokenStore());
tokenService.setSupportRefreshToken(true);
tokenService.setClientDetailsService(endpoints.getClientDetailsService());
tokenService.setTokenEnhancer(endpoints.getTokenEnhancer());
//token有效期 1小时
tokenService.setAccessTokenValiditySeconds(3600);
//token刷新有效期 15天
tokenService.setRefreshTokenValiditySeconds(3600 * 12 * 15);
tokenService.setReuseRefreshToken(false);
return tokenService;
}
/**
* 解决访问/oauth/check_token 403的问题
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 允许表单认证
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
配置资源服务器
/**
* @author Charles
* @module springboot
* @since 2023/6/20 12:14
*/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Autowired
@Qualifier("jwtTokenStore")
TokenStore jwtTokenStore;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
//.authorizeRequests(request -> request.anyRequest().access("@checker.check(authentication,request)"))
//.and()
//放行
.antMatchers("/oauth/**","/login/**","/logout/**", "/sse/**").permitAll()
//其他路径需要role_admin
.anyRequest().hasAnyAuthority("role_admin")
.and()
//表单提交放行
.formLogin().permitAll()
.and()
//csrf关闭
.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println(accessDeniedException);
}
});
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("oauth2-server")
.tokenServices(tokenDefaultServices());
}
/**
* 配置资源服务器如何校验token
* 1. DefaultTokenServices
* 如果认证服务器和资源服务器在同一个服务,则直接采用默认服务验证
* 2.RemoteTokenServices
* 当认证服务器和资源服务器不在同一个服务,要使用此服务器去远程认证服务器验证
* @return
*/
@Primary
@Bean
public DefaultTokenServices tokenDefaultServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(jwtTokenStore);
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
// @Primary
// @Bean
// public RemoteTokenServices tokenServices() {
// //资源服务器去远程认证服务器验证 token 是否有效
// final RemoteTokenServices tokenService = new RemoteTokenServices();
// //请求认证服务器验证URL,注意:默认这个端点是拒绝访问的,要设置认证后可访问
// tokenService.setCheckTokenEndpointUrl("http://localhost:8899/oauth/check_token");
// //在认证服务器配置的客户端id
// tokenService.setClientId("test-pc");
// //在认证服务器配置的客户端密码
// tokenService.setClientSecret("123456");
// return tokenService;
// }
}
token配置
/**
* @author Charles
* @module springboot
* @since 2023/6/20 15:25
*/
@Configuration
public class JwtTokenStoreConfig {
@Autowired
private CustomTokenEnhancer customTokenEnhancer;
@Value("${privateKey}")
private String privateKey;
@Value("${password}")
private String password;
@Value("${alias}")
private String alias;
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(privateKey), password.toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair(alias));
return jwtAccessTokenConverter;
}
@Bean("jwtTokenStore")
public TokenStore jwtTokenStore(){
JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
return jwtTokenStore;
}
@Bean
public TokenEnhancerChain tokenEnhancerChain() {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancers = new ArrayList<>();
enhancers.add(jwtAccessTokenConverter());
enhancers.add(customTokenEnhancer);
//将自定义Enhancer加入EnhancerChain的delegates数组中
enhancerChain.setTokenEnhancers(enhancers);
return enhancerChain;
}
private static final KeyStore JKS_STORE;
static {
try {
JKS_STORE = KeyStore.getInstance("jks");
} catch (KeyStoreException e) {
throw new RuntimeException("can not obtain jks keystore instance");
}
}
@Bean
@ConditionalOnMissingBean
@SneakyThrows
public JWKSource<SecurityContext> jwkSource() {
ClassPathResource classPathResource = new ClassPathResource(privateKey);
char[] pin = password.toCharArray();
JKS_STORE.load(classPathResource.getInputStream(), pin);
RSAKey rsaKey = RSAKey.load(JKS_STORE, alias, pin);
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
配置UserDetailsService
/**
* @author Charles
* @module springboot
* @since 2023/6/20 11:47
*/
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User("admin", passwordEncoder.encode("123456"),
AuthorityUtils.createAuthorityList("role_admin"));
}
}
好了到此为止,以上就是springboot整合ouath2.0的主要代码啦,欢迎大家在评论区留言讨论!