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 方法,从缓存中获取客户端的配置信息
	 */		
	@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 {

	/**
	 * 登录方法,获取token信息
	 */		
	@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("")) {
			// 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 client
				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)) {
			// The scope was requested or determined during the authorization step
			if (!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 {

	@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)))) {
                // 将token 信息加载到缓存中
                storeAccessToken(accessToken, authentication);
            }

        }
        return accessToken;
	}

	@Override
	public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
		// 根据token 与 authentication信息自定义缓存
		// ...
	}

	/**
 	 * 重写方法,自定义根据token获取 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;
    }

	/**
 	 * 重写方法,删除token信息
 	 */	
	@Override
    public OAuth2AccessToken removeAccessToken(String tokenValue){

	}
}

5. 自定义用户信息类

/**
 * 重写类,实现 UserDetailsService 类
 */
public class MyUserDetailServiceImpl implements UserDetailsService {

	@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 自定义:可写mapper查询出用户信息,放入UserDeatail对象中返回
	}
}

6. 异常翻译类

/**
 * 异常翻译 实现 WebResponseExceptionTranslator 类
 */
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 {
	
	/**
     * token 检查策略
     */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    	security.checkTokenAccess("permitAll()");
	}

	/**
     * 客户端配置
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 该对象是2
        clients.withClientDetails(redisClientDetailsService);
    }

	/**
     * token 携带额外信息
     */
    @Override
    public 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
     */
	@Bean
    public TokenStore tokenStore() {
		// 是否开启jwt认证
        if (properties.getEnableJwt()) {
            return new JwtTokenStore(jwtAccessTokenConverter());
        } else {
            // 使用自定义类 对象4
            MyRedisTokenStore redisTokenStore = new MyRedisTokenStore(redisConnectionFactory);
            // 解决每次生成的 token都一样的问题
            redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
            return redisTokenStore;
        }
    }
}

你可能感兴趣的:(spring,spring,boot,java,spring)