您的位置:首页 > 游戏 > 游戏 > springboot 集成OAuth2 一些用到的基础类

springboot 集成OAuth2 一些用到的基础类

2024/11/17 12:26:57 来源:https://blog.csdn.net/weixin_42370032/article/details/141320081  浏览:    关键词:springboot 集成OAuth2 一些用到的基础类

文章目录

      • 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配置信息类

// 自定义类,继承JdbcClientDetailsService 或 ClientDetailsService
public class RedisClientDetailsService extends JdbcClientDetailsService {/*** 重写 loadClientByClientId 方法,从缓存中获取客户端的配置信息*/		@Overridepublic 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 {/*** 登录方法,获取token信息*/		@RequestMapping(value = "/oauth/token", method=RequestMethod.GET)public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParamMap<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, @RequestParamMap<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("")) {// Only validate the client details if a client authenticated during this// request.if (!clientId.equals(tokenRequest.getClientId())) {// double check to make sure that the client ID in the token request is the same as that in the// authenticated clientthrow 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)) {// The scope was requested or determined during the authorization stepif (!tokenRequest.getScope().isEmpty()) {logger.debug("Clearing scope of incoming token request");tokenRequest.setScope(Collections.<String> emptySet());}}if (isRefreshTokenRequest(parameters)) {// A refresh token has its own default scopes, so we should ignore any added by the factory here.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信息到自定义缓存

/*** 重写类,实现 TokenStore 类*/
public class MyRedisTokenStore implements TokenStore {@Overridepublic 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)))) {// 将token 信息加载到缓存中storeAccessToken(accessToken, authentication);}}return accessToken;}@Overridepublic void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {// 根据token 与 authentication信息自定义缓存// ...}/*** 重写方法,自定义根据token获取 authentication 信息*/	@Overridepublic 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;}/*** 重写方法,删除token信息*/	@Overridepublic OAuth2AccessToken removeAccessToken(String tokenValue){}
}

5. 自定义用户信息类

/*** 重写类,实现 UserDetailsService 类*/
public class MyUserDetailServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 自定义:可写mapper查询出用户信息,放入UserDeatail对象中返回}
}

6. 异常翻译类

/*** 异常翻译 实现 WebResponseExceptionTranslator 类*/
public class FebsWebResponseExceptionTranslator implements WebResponseExceptionTranslator {@Overridepublic 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 {/*** token 检查策略*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.checkTokenAccess("permitAll()");}/*** 客户端配置*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 该对象是2clients.withClientDetails(redisClientDetailsService);}/*** token 携带额外信息*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));endpoints.tokenStore(tokenStore()) // token信息记录方式.tokenEnhancer(tokenEnhancerChain).userDetailsService(userDetailService) // 对象5.authorizationCodeServices(authenticationCodeService) // 咱不知道用途.authenticationManager(authenticationManager) // 咱不知道用途.exceptionTranslator(exceptionTranslator); // 对象6// 是否开启jwt认证if (properties.getEnableJwt()) {endpoints.accessTokenConverter(jwtAccessTokenConverter());}}/*** 注入TokenStore*/@Beanpublic TokenStore tokenStore() {// 是否开启jwt认证if (properties.getEnableJwt()) {return new JwtTokenStore(jwtAccessTokenConverter());} else {// 使用自定义类 对象4MyRedisTokenStore redisTokenStore = new MyRedisTokenStore(redisConnectionFactory);// 解决每次生成的 token都一样的问题redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());return redisTokenStore;}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com