文章目录
-
-
- 1. 登录校验token,提取clientId
- 2. 根据clientId 获取token配置信息类
- 3. 实际登录类
- 4. 加载token信息到自定义缓存
- 5. 自定义用户信息类
- 6. 异常翻译类
- 7. 认证服务器配置
1. 登录校验token,提取clientId
package org.springframework.security.web.authentication.www;
public class BasicAuthenticationConverter implements AuthenticationConverter {
public UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header == null) {
return null;
} else {
header = header.trim();
if (!StringUtils.startsWithIgnoreCase(header, "Basic")) {
return null;
} else if (header.equalsIgnoreCase("Basic")) {
throw new BadCredentialsException("Empty basic authentication token");
} else {
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded = this.decode(base64Token);
String token = new String(decoded, this.getCredentialsCharset(request));
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
} else {
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.unauthenticated(token.substring(0, delim), token.substring(delim + 1));
result.setDetails(this.authenticationDetailsSource.buildDetails(request));
return result;
}
}
}
}
}
2. 根据clientId 获取token配置信息类
public class RedisClientDetailsService extends JdbcClientDetailsService {
@Override
public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
ClientDetails clientDetails = null;
String value = RedisUtil.hGet(CACHE_CLIENT_KEY, clientId, RedisDBType.PRIMARY);
if (StringUtils.isBlank(value)) {
clientDetails = cacheAndGetClient(clientId);
} else {
clientDetails = JSONObject.parseObject(value, BaseClientDetails.class);
}
return clientDetails;
}
}
3. 实际登录类
package org.springframework.security.oauth2.provider.endpoint;
public class TokenEndpoint extends AbstractEndpoint {
@RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
throw new HttpRequestMethodNotSupportedException("GET");
}
return postAccessToken(principal, parameters);
}
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
String clientId = getClientId(principal);
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
if (clientId != null && !clientId.equals("")) {
if (!clientId.equals(tokenRequest.getClientId())) {
throw new InvalidClientException("Given client ID does not match authenticated client");
}
}
if (authenticatedClient != null) {
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
if (!StringUtils.hasText(tokenRequest.getGrantType())) {
throw new InvalidRequestException("Missing grant type");
}
if (tokenRequest.getGrantType().equals("implicit")) {
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
}
if (isAuthCodeRequest(parameters)) {
if (!tokenRequest.getScope().isEmpty()) {
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.<String> emptySet());
}
}
if (isRefreshTokenRequest(parameters)) {
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}
return getResponse(token);
}
}
4. 加载token信息到自定义缓存
public class MyRedisTokenStore implements TokenStore {
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = serializeKey(FebsConstant.AUTH_TO_ACCESS + key);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
bytes = conn.get(serializedKey);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue());
if ((storedAuthentication == null || !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) {
storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
}
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
bytes = conn.get(serializeKey(FebsConstant.AUTH + token));
} finally {
conn.close();
}
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
}
@Override
public OAuth2AccessToken removeAccessToken(String tokenValue){
}
}
5. 自定义用户信息类
public class MyUserDetailServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
}
}
6. 异常翻译类
public class FebsWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
@Override
public ResponseEntity<?> translate(Exception e) {
ResponseEntity.BodyBuilder status = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
FebsResponse response = new FebsResponse();
String message = "认证失败";
log.error(message, e);
if (e instanceof UnsupportedGrantTypeException) {
message = "不支持该认证类型";
return status.body(response.message(message));
}
if (e instanceof InvalidTokenException
&& StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token (expired)")) {
message = "刷新令牌已过期,请重新登录";
return status.body(response.message(message));
}
if (e instanceof InvalidScopeException) {
message = "不是有效的scope值";
return status.body(response.message(message));
}
if (e instanceof RedirectMismatchException) {
message = "redirect_uri值不正确";
return status.body(response.message(message));
}
if (e instanceof BadClientCredentialsException) {
message = "client值不合法";
return status.body(response.message(message));
}
if (e instanceof UnsupportedResponseTypeException) {
String code = StringUtils.substringBetween(e.getMessage(), "[", "]");
message = code + "不是合法的response_type值";
return status.body(response.message(message));
}
if (e instanceof InvalidGrantException) {
if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token")) {
message = "refresh token无效";
return status.body(response.message(message));
}
if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid authorization code")) {
String code = StringUtils.substringAfterLast(e.getMessage(), ": ");
message = "授权码" + code + "不合法";
return status.body(response.message(message));
}
if (StringUtils.containsIgnoreCase(e.getMessage(), "locked")) {
message = "用户已被锁定,请联系管理员";
return status.body(response.message(message));
}
message = "用户名或密码错误";
return status.body(response.message(message));
}
return status.body(response.message(message));
}
}
7. 认证服务器配置
public class MyAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(redisClientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.userDetailsService(userDetailService)
.authorizationCodeServices(authenticationCodeService)
.authenticationManager(authenticationManager)
.exceptionTranslator(exceptionTranslator);
if (properties.getEnableJwt()) {
endpoints.accessTokenConverter(jwtAccessTokenConverter());
}
}
@Bean
public TokenStore tokenStore() {
if (properties.getEnableJwt()) {
return new JwtTokenStore(jwtAccessTokenConverter());
} else {
MyRedisTokenStore redisTokenStore = new MyRedisTokenStore(redisConnectionFactory);
redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
return redisTokenStore;
}
}
}