撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>>
Spring security oauth2资源的认证模式
ResourceServerSecurityConfigurer资源配置模式
代码语言:javascript复制@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
if (eventPublisher != null) {
resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
}
if (tokenExtractor != null) {
//添加token的额外解析方法 默认调用BearerTokenExtractor
resourcesServerFilter.setTokenExtractor(tokenExtractor);
}
if (authenticationDetailsSource != null) {
resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
resourcesServerFilter = postProcess(resourcesServerFilter);
resourcesServerFilter.setStateless(stateless);
// @formatter:off
http
.authorizeRequests().expressionHandler(expressionHandler)
.and()
.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
// @formatter:on
}
OAuth2AuthenticationProcessingFilter中作为filter拦截认证会借助tokenExtractor从request中获取token的值,将其转换为Authentication 对象。
代码语言:javascript复制public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
...
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
//调用TokenExtractor从httpRequest解析出对应的token值,将其转化为Authentication对象。
Authentication authentication = tokenExtractor.extract(request);
if (authentication == null) {
if (stateless && isAuthenticated()) {
if (debug) {
logger.debug("Clearing security context.");
}
SecurityContextHolder.clearContext();
}
if (debug) {
logger.debug("No token in request, will continue chain.");
}
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
//2调用的Authenticationd对象,调用authenticationManager.authenticate的方法
来判断用户是否登陆成功
Authentication authResult = authenticationManager.authenticate(authentication);
if (debug) {
logger.debug("Authentication success: " authResult);
}
eventPublisher.publishAuthenticationSuccess(authResult);
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
if (debug) {
logger.debug("Authentication request failed: " failed);
}
eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException(failed.getMessage(), failed));
return;
}
chain.doFilter(request, response);
}
}
....
}
认证执行结束之后,继续走configure中的配置的权限认证过滤操作 AuthenticationManager 默认实现方式是配置的OAuth2AuthenticationManager,所以OAuth2AuthenticationManager中的
代码语言:javascript复制--ResourceServerSecurityConfigurer.java
private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
if (authenticationManager != null) {
if (authenticationManager instanceof OAuth2AuthenticationManager) {
oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager;
}
else {
return authenticationManager;
}
}
oauthAuthenticationManager.setResourceId(resourceId);
//配置tokenService解析方式 oauthAuthenticationManager.setTokenServices(resourceTokenServices(http));
oauthAuthenticationManager.setClientDetailsService(clientDetails());
return oauthAuthenticationManager;
}
private ResourceServerTokenServices resourceTokenServices(HttpSecurity http) {
tokenServices(http);
return this.resourceTokenServices;
}
private ResourceServerTokenServices tokenServices(HttpSecurity http) {
if (resourceTokenServices != null) {
return resourceTokenServices;
}
DefaultTokenServices tokenServices = new DefaultTokenServices();
//指定token的解析方式
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetails());
this.resourceTokenServices = tokenServices;
return tokenServices;
}
Token的存取模式:
种模式
- InMemoryTokenStore
- JdbcTokenStore
- JwtTokenStore
- JwkTokenStore
- RedisTokenStore
--OAuth2AuthenticationManager认证管理
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
}
String token = (String) authentication.getPrincipal();
//从指定的实现的tokenStore中获取对应的值
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " token);
}
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" resourceId ")");
}
checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
// Guard against a cached copy of the same details
if (!details.equals(auth.getDetails())) {
// Preserve the authentication details from the one loaded by token services
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}