一:配置多个Client并持久化到Redis中
@Data
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
public class OAuth2Client {
private String clientId;
private String clientSecret;
private int accessTokenValiditySeconds;
}
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
List<OAuth2Client> clientList = Arrays.asList(
new OAuth2Client("clientId", "clientSecret", 7200),
new OAuth2Client("clientId2", "clientSecret2", 0)
);
InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
// 可以通过多次调用withClient来配置多对clientId和secret
clientList.forEach(client -> {
inMemory // 使用in-memory存储
.withClient(client.getClientId())
.secret(new BCryptPasswordEncoder().encode(client.getClientSecret()))
// token的生效时间2小时, 0 表示永久生效不过期
.accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
.authorizedGrantTypes("authorization_code", "refresh_token", "password", "implicit")
.scopes("all", "read", "write")
.redirectUris("http://www.baidu.com");
});
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(redisTokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Bean
public TokenStore redisTokenStore() {
return new RedisTokenStore(redisConnectionFactory());
}
public RedisConnectionFactory redisConnectionFactory() {
return new JedisConnectionFactory();
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 解决报错:'AuthenticationManager' that could not be found
* org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
* @return
* @throws Exception
*/
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
二:配置JWT
oauth2中生成的token是一个uuid值,其值本身是没有任何意义的,我们可以将替换成json web token, 简称 JWT
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
认证服务器
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
/**
* 这里会注入失败
*/
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
List<OAuth2Client> clientList = Arrays.asList(
new OAuth2Client("clientId", "clientSecret", 7200),
new OAuth2Client("clientId2", "clientSecret2", 0)
);
InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
// 可以通过多次调用withClient来配置多对clientId和secret
clientList.forEach(client -> {
inMemory // 使用in-memory存储
.withClient(client.getClientId())
.secret(new BCryptPasswordEncoder().encode(client.getClientSecret()))
// token的生效时间2小时, 0 表示永久生效不过期
.accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
.refreshTokenValiditySeconds(259200)
.authorizedGrantTypes("authorization_code", "refresh_token", "password", "implicit")
.scopes("all", "read", "write")
.redirectUris("http://www.baidu.com");
});
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.userDetailsService(new MyUserDetailsService(new BCryptPasswordEncoder()));
// jwt增强器
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancers = new ArrayList<>();
enhancers.add(jwtTokenEnhancer());
enhancers.add(jwtAccessTokenConverter());
enhancerChain.setTokenEnhancers(enhancers);
endpoints
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter());
}
/**
* 将token存储到redis中
* @return
*/
// @Bean
// public TokenStore tokenStore() {
// return new RedisTokenStore(redisConnectionFactory());
// }
//
// public RedisConnectionFactory redisConnectionFactory() {
// return new JedisConnectionFactory();
// }
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("myJwtKey");
return accessTokenConverter;
}
/**
* token增强器
* @return
*/
@Bean
public TokenEnhancer jwtTokenEnhancer() {
return new JwtTokenEnhancer();
}
}
Token增强器
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
// 在jwt中添加自定义字段
Map<String, Object> info = new HashMap<>();
info.put("foo", "bar");
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
return accessToken;
}
}
@Slf4j
@RestController
public class UserController {
@Autowired
private ObjectMapper objectMapper;
@GetMapping(value = "/user/me", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String me(Authentication auth, HttpServletRequest request) throws JsonProcessingException, UnsupportedEncodingException {
String header = request.getHeader("Authorization");
String token = header.split("bearer ")[1];
Claims claims = Jwts.parser().setSigningKey("myJwtKey".getBytes("UTF-8")).parseClaimsJws(token).getBody();
String foo = (String) claims.get("foo");
System.out.println(foo);
return objectMapper.writeValueAsString(auth);
}
}
获取token
jwt access_token字符串可以通过https://www.jsonwebtoken.io/进行解析
通过access_token访问用户信息
访问oauth/token并传参refresh_token获取新的access_token