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; // } }
/** * @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(); Listenhancers = 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 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); }
/** * @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的主要代码啦,欢迎大家在评论区留言讨论!