Spring Oauth2
0
最近在看Spring Oauth2的东西,开始看的非常迷糊,后来才有点点找到门路。
先看下现在的Spring Oauth2依赖:
spring-security-oauth2
spring-cloud-starter-oauth2
spring-security-oauth2-client
spring-security-oauth2-resource-server
spring-security-oauth2-authorization-server
spring-boot-starter-oauth2-client
spring-boot-starter-oauth2-resource-server
兄弟们是不是非常乱,spring-security-oauth2
已经标注废弃。
spring-cloud-starter-oauth2
网上代码基本上都是通过这个实现的,基于spring-security-oauth2
实现,所以废弃应该是迟早的事情。
官方现在推荐使用的应该是spring-security-oauth2
开头的这些,下面的spring-boot-starter-oauth2
对应包含上面spring-security-oauth2
依赖。
spring-security-oauth2-autoconfigure
虽然这个已经废弃使用,但是还是记录一下,如果授权服务器和资源服务器是同一个模块,直接使用相同的TokenStore
,可以忽略一些配置:
security:
oauth2:
client:
client-id: clientApp
client-secret: secret
scope: all
# 使用相同TokenStore不用配置Token校验(如果不同可以换为JWT和JWK配置提高性能)
# resource:
# token-info-uri: https://localhost:8082/oauth/check_token
认证服务器配置
/**
* 认证
*
* @author yusheng
*/
@Configuration
@SuppressWarnings("deprecation")
@EnableAuthorizationServer
public class AuthorizationServerAutoConfiguration extends AuthorizationServerConfigurerAdapter {
@Value("${camera.oauth.jwt.file:}")
private String jwtFile;
@Value("${camera.oauth.jwt.alias:}")
private String jwtAlias;
@Value("${camera.oauth.jwt.password:}")
private String jwtPassword;
@Value("${camera.oauth.jwt.access.days:30}")
private int jwtAccessDays;
@Value("${camera.oauth.jwt.refresh.days:180}")
private int jwtRefreshDays;
@Autowired
private DataSource dataSource;
@Autowired
private TokenStore tokenStore;
@Autowired
public PasswordEncoder passwordEncoder;
@Autowired(required = false)
private OAuth2RequestFactory requestFactory;
@Autowired
public UserDetailsService userDetailsService;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired(required = false)
private AuthorizationCodeServices authorizationCodeServices;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(this.dataSource);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.allowFormAuthenticationForClients()
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
OAuth2RequestFactory oAuth2RequestFactory = this.requestFactory;
if(oAuth2RequestFactory == null) {
oAuth2RequestFactory = new DefaultOAuth2RequestFactory(this.clientDetailsService);
}
AuthorizationCodeServices authorizationCodeServices = this.authorizationCodeServices;
if(authorizationCodeServices == null) {
authorizationCodeServices = new InMemoryAuthorizationCodeServices();
}
final AuthorizationServerTokenServices authorizationServerTokenServices = this.tokenService();
final List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new RefreshTokenGranter(authorizationServerTokenServices, this.clientDetailsService, oAuth2RequestFactory));
// tokenGranters.add(new ImplicitTokenGranter(authorizationServerTokenServices, this.clientDetailsService, requestFactory));
// tokenGranters.add(new ClientCredentialsTokenGranter(authorizationServerTokenServices, this.clientDetailsService, requestFactory));
tokenGranters.add(new AuthorizationCodeTokenGranter(authorizationServerTokenServices, authorizationCodeServices, this.clientDetailsService, oAuth2RequestFactory));
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(this.authenticationManager, authorizationServerTokenServices, this.clientDetailsService, oAuth2RequestFactory));
endpoints
.tokenGranter(new CompositeTokenGranter(tokenGranters))
.tokenServices(authorizationServerTokenServices)
.userDetailsService(this.userDetailsService)
.exceptionTranslator(new MessageCodeTranslator())
.authenticationManager(this.authenticationManager);
}
private AuthorizationServerTokenServices tokenService() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(this.tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenEnhancer(this.jwtAccessTokenConverter());
tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(this.jwtAccessDays));
tokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(this.jwtRefreshDays));
return tokenServices;
}
@Bean
@ConditionalOnMissingBean
public TokenKeyEndpoint tokenKeyEndpoint() {
return new TokenKeyEndpoint(this.jwtAccessTokenConverter());
}
@Bean
@ConditionalOnMissingBean
public TokenStore tokenStore() {
return new JwtTokenStore(this.jwtAccessTokenConverter());
}
private JwtAccessTokenConverter jwtAccessTokenConverter() {
final KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(
new ClassPathResource(this.jwtFile),
this.jwtPassword.toCharArray()
);
// Token增强
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Object principal = authentication.getPrincipal();
if (principal instanceof Principal) {
final Principal target = (Principal) principal;
final Map<String, Object> info = new HashMap<>();
info.put("id", target.getId());
info.put("user_name_cn", target.getUsernameCn());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
}
return super.enhance(accessToken, authentication);
}
};
converter.setKeyPair(keyStoreKeyFactory.getKeyPair(this.jwtAlias));
return converter;
}
@Bean
@ConditionalOnMissingBean
public DaoAuthenticationProvider daoAuthenticationProvider() throws Exception {
final DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(this.passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
daoAuthenticationProvider.afterPropertiesSet();
return daoAuthenticationProvider;
}
}
资源服务器配置
/**
* 资源
*
* @author yusheng
*/
@Configuration
@SuppressWarnings("deprecation")
@EnableWebSecurity
@ConditionalOnClass(EnableResourceServer.class)
@EnableResourceServer
public class ResourceServerAutoConfiguration extends ResourceServerConfigurerAdapter {
@Value("#{'${camera.permit.url:}'.split(',')}")
private String[] permitUrl;
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// 错误
resources
.stateless(true)
.tokenStore(this.tokenStore)
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// throw MessageCodeException.of(authException, MessageCode.CODE_3401, "没有授权");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(Message.fail(MessageCode.CODE_3401, "没有授权").toString());
}
});
}
@Override
public void configure(HttpSecurity security) throws Exception {
security
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(
// 错误
"/error",
// 图标
"/favicon.ico",
// swagger
"/v3/api-docs", "/swagger-ui/**", "/swagger-resources/**"
).permitAll()
// 配置允许URL
.antMatchers(this.permitUrl).permitAll()
.anyRequest().authenticated();
}
}