赞
踩
ManagedFilter管理这六条过滤器链,如图所示:
其中的第五条过滤器链springSecurityFilterChain是本文要讨论的对象。
0.characterEncodingFilter
1.WebMvcMetricsFilter
2.formContentFilter
3.requestContextFilter
4.springSecurityFilterChain
5.corsFilter
ManagedFilter.class -> doFilter()
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (this.servletContext.getDeployment().getDeploymentState() != State.STARTED) {
throw UndertowServletMessages.MESSAGES.deploymentStopped(this.servletContext.getDeployment().getDeploymentInfo().getDeploymentName());
} else {
if (!this.started) {
this.start();
}
this.getFilter().doFilter(request, response, chain);//迭代下一条过滤器链
}
}
在springSecurityFilterChain过滤器链中,首先初始化一个FilterChainProxy过滤器链代理对象,在这个过滤器链代理对象中有一个过滤器链集合,每一个过滤器链都有一组过滤器来处理不同的请求。
其中的第八条过滤器链OAuth2AuthenticationProcessingFilter是本文要讨论的对象,用于处理请求的鉴权。
FilterChainProxy.class --> doFilter()
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (this.currentPosition == this.size) { if (FilterChainProxy.logger.isDebugEnabled()) { FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); } this.firewalledRequest.reset(); this.originalChain.doFilter(request, response); } else { ++this.currentPosition; Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1);//new了一个过滤器链代理对象 if (FilterChainProxy.logger.isDebugEnabled()) { FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " at position " + this.currentPosition + " of " + this.size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); //迭代过滤器 } }
OAuth2鉴权主要用到了OAuth2AuthenticationProcessingFilter过滤器。这个过滤器下涉及到的类有以下12个类,大致作用是从请求中提取authentication,从authentication中获取token,判断是否为空,是否有效,是否过期。具体主要包含1-66个步骤的跳转和调用,详见代码中的注释。
【1】OAuth2AuthenticationProcessingFilter.class
这里是鉴权开始执行的起点,鉴权的大致步骤为以下内容:从请求中提取authentication --> 判断是否为空 --> 清除SpringSecurity上下文中的内容(如果有) --> 存到request的attributte中 --> 由authenticationManager进行鉴权操作 --> 将鉴权通过的事件进行广播 --> 将鉴权结果存储到SpringSecurity上下文中。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { boolean debug = logger.isDebugEnabled(); HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; try { //1.从请求中提取authentication,跳到【2】 Authentication authentication = this.tokenExtractor.extract(request); //23.判断authentication是否为空,如果为空,也就是这个请求未携带token if (authentication == null) { //24.如果this.stateless等于true,同时 SecurityContextHolder.getContext().getAuthentication()中的authentication不为空而且不属于匿名用户的authentication if (this.stateless && this.isAuthenticated()) { if (debug) { logger.debug("Clearing security context."); } SecurityContextHolder.clearContext(); } if (debug) { logger.debug("No token in request, will continue chain."); } //25.如果请求中提取的authentication不为空 } else { //26.把OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE作为key,authentication.getPrincipal()作为value存到request的attributte中 request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal()); //27.判断authentication的对象是否是AbstractAuthenticationToken的实例 if (authentication instanceof AbstractAuthenticationToken) { //28.如果是,Authentication authentication向下转型为AbstractAuthenticationToken needsDetails AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken)authentication; //29.为needsDetails的details属性赋值,跳到【4】 needsDetails.setDetails(this.authenticationDetailsSource.buildDetails(request)); } //31.跳到【5】,由authenticationManager进行鉴权后返回authResult,从【5】返回这里并开始下一行 Authentication authResult = this.authenticationManager.authenticate(authentication); if (debug) { logger.debug("Authentication success: " + authResult); } //64.将鉴权通过的事件进行广播 this.eventPublisher.publishAuthenticationSuccess(authResult); //65.将authResult存到SecurityContextHolder的context属性中 SecurityContextHolder.getContext().setAuthentication(authResult); } } catch (OAuth2Exception var9) { SecurityContextHolder.clearContext(); if (debug) { logger.debug("Authentication request failed: " + var9); } this.eventPublisher.publishAuthenticationFailure(new BadCredentialsException(var9.getMessage(), var9), new PreAuthenticatedAuthenticationToken("access-token", "N/A")); this.authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(var9.getMessage(), var9)); return; } //66.放行 chain.doFilter(request, response); } private boolean isAuthenticated() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authentication != null && !(authentication instanceof AnonymousAuthenticationToken); }
【2】CookieTokenExtractor.class(我们自己重写的方法)
【2】~ 【3】这两个类里主要是从请求中获取authentication的具体实现步骤:大致上分为从SpringSecurity上下文中获取、从cookie中获取、从请求头中获取。从任意一个位置获取之后就把authentication返回到【1】。
@Override public Authentication extract(HttpServletRequest request) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null) {//2.从SecurityContextHolder中提取authentication return authentication;//3.如果拿到了,直接返回提取到的authentication } //4.如果没拿到,调用父类extract(request)方法,跳到【3】 return super.extract(request); //22.带着token跳到【1】 } @Override protected String extractToken(HttpServletRequest request) { String result; //6.从cookie中提取authentication Cookie accessTokenCookie = OAuth2CookieHelper.getAccessTokenCookie(request); //7.如果拿到了,直接返回提取到的authentication if (accessTokenCookie != null) { result = accessTokenCookie.getValue(); } else { //8.如果没拿到,调用父类extractToken(request)方法,跳到【3】 result = super.extractToken(request); } //19.带着token,跳到C中的extract(HttpServletRequest request) return result; }
【3】BearerTokenExtractor.class -> extract()
public Authentication extract(HttpServletRequest request) { //5.调用子类中的extractToken(request),从请求中获取token String tokenValue = this.extractToken(request); //20.如果获取到了,包装token if (tokenValue != null) { PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, ""); //21.返回包装后的authentication,跳到【2】 return authentication; } else { return null; } } protected String extractToken(HttpServletRequest request) { //9.调用本类中extractHeaderToken(request),从请求中获取token String token = this.extractHeaderToken(request); if (token == null) { logger.debug("Token not found in headers. Trying request parameters."); token = request.getParameter("access_token"); if (token == null) { logger.debug("Token not found in request parameters. Not an OAuth2 request."); } else { request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, "Bearer"); } } //18.带着token,跳到【2】 return token; } protected String extractHeaderToken(HttpServletRequest request) { //10.通过请求头的key【Authorization】获取对应的值 Enumeration<String> headers = request.getHeaders("Authorization"); String value; do {//11.遍历获取到的值 if (!headers.hasMoreElements()) { return null; } value = (String)headers.nextElement(); //12.找到一个以"Bearer"开头的value,把这个value转换成全小写字母 } while(!value.toLowerCase().startsWith("Bearer".toLowerCase())); //13.把这个value前面的"Bearer"裁掉并且去空格处理,存储到authHeaderValue中 String authHeaderValue = value.substring("Bearer".length()).trim(); //14.把OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE作为key,"Bearer"作为value存到request的attributte中 request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, value.substring(0, "Bearer".length()).trim()); //15.找到数字44在authHeaderValue中的下标 int commaIndex = authHeaderValue.indexOf(44); if (commaIndex > 0) { //16.如果下标>0,也就是存在这个数字,就剪切开头到下标44所在的位置的值,重新赋给authHeaderValue authHeaderValue = authHeaderValue.substring(0, commaIndex); } //17.否则直接返回authHeaderValue,跳到本类中的extractToken(HttpServletRequest request) return authHeaderValue; }
【4】OAuth2AuthenticationDetailsSource.class
这里创建了OAuth2AuthenticationDetails对象,把request存放进去
@Deprecated
public class OAuth2AuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, OAuth2AuthenticationDetails> {
public OAuth2AuthenticationDetailsSource() {
}
public OAuth2AuthenticationDetails buildDetails(HttpServletRequest context) {
//30.new 了一个OAuth2AuthenticationDetails对象,把request存放进去,然后返回【1】
return new OAuth2AuthenticationDetails(context);
}
}
【5】OAuth2AuthenticationManager.class
这里是鉴权的核心处理器:从authentication中获取token后,解析token,并对解析后的token分别执行了是否为空判断、有效性和是否过期判断
public Authentication authenticate(Authentication authentication) throws AuthenticationException { //32.判断authentication是否为空,为空就抛异常 if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } else { //33.从authentication中获取token String token = (String)authentication.getPrincipal(); //34.校验token的有效性和是否过期,跳到【6】 OAuth2Authentication auth = this.tokenServices.loadAuthentication(token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token); } else { Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds(); if (this.resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(this.resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + this.resourceId + ")"); } else { //61.调用本类中checkClientDetails(auth)方法 this.checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails(); if (!details.equals(auth.getDetails())) { details.setDecodedDetails(auth.getDetails()); } } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); //63.返回处理之后的auth,跳到【1】 return auth; } } } } private void checkClientDetails(OAuth2Authentication auth) { //62.Oauth客户端如果为null ,不检查,如果配置了ClientDetailsService的实现类,会进行检查 if (this.clientDetailsService != null) { ClientDetails client; try { //获取客户端 client = this.clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId()); } catch (ClientRegistrationException var6) { throw new OAuth2AccessDeniedException("Invalid token contains invalid client id"); } //获取客户端的授权范围 Set<String> allowed = client.getScope(); Iterator var4 = auth.getOAuth2Request().getScope().iterator(); //迭代授权范围,如果没有当前授权,就抛异常 while(var4.hasNext()) { String scope = (String)var4.next(); if (!allowed.contains(scope)) { throw new OAuth2AccessDeniedException("Invalid token contains disallowed scope (" + scope + ") for this client"); } } } }
【6】DefaultTokenServices.class -> loadAuthentication(String accessTokenValue)
这里做两件事情:
1.是解析token,即把token解析为可以阅读的键值对
2.是对于token的为空判断、有效性和是否过期判断的具体步骤
public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException { //35.从解析token,跳到【7】 OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(accessTokenValue); //51.判断token是否为空,为空就抛异常 if (accessToken == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue); //53.判断token是否过期,过期就从tokenStore中移除token并且抛异常,跳到【11】 } else if (accessToken.isExpired()) { this.tokenStore.removeAccessToken(accessToken); throw new InvalidTokenException("Access token expired: " + accessTokenValue); } else { //55.从tokenStore中解析token,跳到【7】 OAuth2Authentication result = this.tokenStore.readAuthentication(accessToken); //59.结果为空就抛异常 if (result == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue); } else { if (this.clientDetailsService != null) { String clientId = result.getOAuth2Request().getClientId(); try { this.clientDetailsService.loadClientByClientId(clientId); } catch (ClientRegistrationException var6) { throw new InvalidTokenException("Client not valid: " + clientId, var6); } } //60.把解析后的token返回【5】 return result; } } }
【7】JwtTokenStore.class -> readAccessToken(String tokenValue)
【7】~【10】解析token的具体步骤
public OAuth2AccessToken readAccessToken(String tokenValue) { //36.从本类中调用convertAccessToken(tokenValue)读取 OAuth2AccessToken accessToken = this.convertAccessToken(tokenValue); //48.接收到返回的accessToken,调用this.jwtTokenEnhancer.isRefreshToken(accessToken)方法,跳到【9】 if (this.jwtTokenEnhancer.isRefreshToken(accessToken)) { throw new InvalidTokenException("Encoded token is a refresh token"); } else { //50.将token返回【6】 return accessToken; } } private OAuth2AccessToken convertAccessToken(String tokenValue) { //37.调用this.jwtTokenEnhancer.decode(tokenValue)方法用公钥解码token,跳到【8】 //41.调用this.jwtTokenEnhancer.extractAccessToken()方法,跳到【9】 return this.jwtTokenEnhancer.extractAccessToken(tokenValue, this.jwtTokenEnhancer.decode(tokenValue)); //47.接收到返回的token,并将其返回到上面的方法 } public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { //56.跳到下面的方法 return this.readAuthentication(token.getValue()); //58.带着token返回【6】 } public OAuth2Authentication readAuthentication(String token) { //57.调用this.jwtTokenEnhancer.decode(token)方法,跳到【8】,并且把步骤38-47再走一遍,返回一个token,到上面的方法 return this.jwtTokenEnhancer.extractAuthentication(this.jwtTokenEnhancer.decode(token)); }
【8】OAuth2JwtAccessTokenConverter -> decode(String token) 我们重写的方法,这里略
@Override
protected Map<String, Object> decode(String token) {
......
//38.调用父类的解码方法,跳到【9】
return super.decode(token);
//40.从10带着解析后的token返回【7】
......
}
【9】JwtAccessTokenConverter.class -> decode(String token)
protected Map<String, Object> decode(String token) { //39.把token解析为可以阅读的键值对并且返回【8】 try { Jwt jwt = JwtHelper.decodeAndVerify(token, this.verifier); String claimsStr = jwt.getClaims(); Map<String, Object> claims = this.objectMapper.parseMap(claimsStr); if (claims.containsKey("exp") && claims.get("exp") instanceof Integer) { Integer intValue = (Integer)claims.get("exp"); claims.put("exp", new Long((long)intValue)); } this.getJwtClaimsSetVerifier().verify(claims); return claims; } catch (Exception var6) { throw new InvalidTokenException("Cannot convert access token to JSON", var6); } } public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map) { //42.跳到【10】 return this.tokenConverter.extractAccessToken(value, map); //46.接收到返回的token,并将其返回到【7】 } public boolean isRefreshToken(OAuth2AccessToken token) { //49.判断token的additionalInformation属性中是否包含叫做"ati"的key,并将结果返回8 return token.getAdditionalInformation().containsKey("ati"); }
【10】DefaultAccessTokenConverter.class -> extractAccessToken()
public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(value); Map<String, Object> info = new HashMap(map); info.remove("exp"); info.remove("aud"); info.remove(this.clientIdAttribute); info.remove(this.scopeAttribute); //43.把exp字段的数据(毫秒值)转换成日期类型数据 if (map.containsKey("exp")) { token.setExpiration(new Date((Long)map.get("exp") * 1000L)); } if (map.containsKey("jti")) { info.put("jti", map.get("jti")); } token.setScope(this.extractScope(map)); //44.把解析后的token键值对存到token的additionalInformation属性中 token.setAdditionalInformation(info); //45.返回token到【9】 return token; }
【11】DefaultOAuth2AccessToken.class -> isExpired()
判断token是否过期
public boolean isExpired() {
//54.判断accessToken的expiration是否不为空同时小于当前时间,并将结果返回【6】
return this.expiration != null && this.expiration.before(new Date());
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。