关于OAuth的介绍查看我的另一篇文章OAuth的4种授权方式,spring-security-oauth2是实现OAuth2.0的框架,配置稍微有些繁琐,因此本文记录下大概的思路,加深印象。
OAuth 2.0中主要有Authorization Service授权服务和Resource Service资源服务,他们可以在同一个应用程序中,也可以在两个应用程序中,甚至多个资源服务共享一个授权服务。
spring-security提供了相应的endpoints来管理token的请求,/oauth/authorize端点负责授权服务,/oauth/token端点负责token的请求服务;资源服务中的过滤器OAuth2AuthenticationProcessingFilter 负责校验Token。
下面从总体上介绍授权服务和资源服务的主要配置,详细的配置在githubspring-security-oauth demo上。
授权服务配置
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "openapi";
@Autowired
private OrderAuthProperties orderAuthProperties;
/**
* 注意这里AuthenticationManager和UserAccountService
* 是在SecurityConfiguration配置的,把俩个配置类关联了起来
*/
@Autowired
AuthenticationManager authenticationManager;
@Autowired
private UserAccountService userAccountService;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
private OAuthClientDetailsService oAuthClientDetailsService;
@Autowired
private DataSource dataSource;
/**
* 配置第三方客户端的信息,可以从内存中加载,也可以从数据库加载(更常用)
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
clients.withClientDetails(oAuthClientDetailsService);
// 这里将第三方客户端放到内存里,配置两个客户端,一个用于client认证
// clients.inMemory()
// .withClient("client_1")
// .resourceIds(DEMO_RESOURCE_ID)
// .authorizedGrantTypes("client_credentials", "refresh_token")
// .scopes("select")
// .authorities("client")
// .secret("123456")
// .and().withClient("client_2")
// .secret("123456")
// .resourceIds(DEMO_RESOURCE_ID)
// .authorizedGrantTypes("authorization_code", "code", "password", "refresh_token")
// .scopes("select")
// .redirectUris("http://www.baidu.com");
}
/**
* 定义token endpoint的安全配置
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.realm("oauth2-resources")
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(clientPasswordEncoder())
.allowFormAuthenticationForClients();
}
/**
* 第三方客户端使用加密方式
*/
@Bean
public PasswordEncoder clientPasswordEncoder() {
// 不做加密处理,明文存储
return NoOpPasswordEncoder.getInstance();
}
/**
* 定义授权、token endpoint和token服务,即如何生成token和token存储在哪里(内存/数据库/JWT)
*
* <p>
* 这里最重要的就是DefaultTokenServices,默认是生成随机值作为token
* <p>
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
// 这里设置了两个TokenEnhancer,可改变token值,它会在token生成后/保存前调用
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.authenticationManager(authenticationManager) // 注入authenticationManager开启密码授权模式
// 必须要配置userDetailsService,才支持refresh token grant,to ensure that the account is still active
// 这里和SecurityConfiguration的userAccountService也可以不一样,为什么?
.userDetailsService(userAccountService)
.authorizationCodeServices(authorizationCodeServices) // 定义authorizationCodeServices支持auth code grant.
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain);
}
/**
* TokenEnhancer
*
* JWT提供的,帮助把OAuth认证信息转为JWT,即access_token,它返回的很多默认字段(jti,ati)都是在这里定义的
*/
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 这里使用的密钥也要定义在资源服务里,以便资源服务也可以校验token,否则认证服务就要提供校验token的接口给资源服务
converter.setSigningKey(orderAuthProperties.getOauthJwtSecret());
return converter;
}
/**
* TokenEnhancer
*
* 向token里添加自定义信息
*/
@Bean
public JwtTokenEnhancer tokenEnhancer() {
return new JwtTokenEnhancer();
}
/**
* 定义token的存储方式:可以放在redis/数据库(oauth_access_token表)/内存,或者jwt中
* 这里放在JWT里,根本就不必后端存储token了,这是JWT很大的优势
*
* 但是JWT也有缺点,1.不容易撤销授权,所以一般令牌时效性很短,撤销授权可以在刷新时实现,怎么实现?
* 2.如果要存储的信息很多,令牌会变得很大
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
版权声明:本文为java_collect原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。