zoukankan      html  css  js  c++  java
  • Oauth2的资源服务器核心源码分析

    资源服务器核心源码分析

    OAuth2AuthenticationProcessingFilter

    资源服务器的核心是OAuth2AuthenticationProcessingFilter过滤器。它被插到配置为资源端口的过滤器链中,主要功能是获取请求中携带的access_token中,通过access_token提取OAuth2Authentication并存入Spring Security上下文。

    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 {
                           //提取请求携带的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));
    				}
                                    //获取token携带的认证信息,OAuth2AuthenticationManager主要做了3件事
                                   //1.通过token获取用户的OAuth2Authentication对象
                                  //2.验证访问资源resourceId是否符合范围
                                 //3.验证客户端访问的scope
    				Authentication authResult = authenticationManager.authenticate(authentication);
    
    				if (debug) {
    					logger.debug("Authentication success: " + authResult);
    				}
    
    				eventPublisher.publishAuthenticationSuccess(authResult);
                                    //将当前的Authentication放入到Context中,访问后面的资源
    				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);
    	}
    
    • TokenExtractor的默认实现类BearerTokenExtractor
    • AuthenticationManager的默认实现类是OAuth2AuthenticationManager

    TokenExtractor

    接口的功能是提取请求中包含的access_token,目前只有一个实现类:BearerTokenExtractor,它只用于提取Bearer类型的access_token。请求中携带的access_token参数即可以放在HTTP请求头中,也可以在HTTP请求参数中。核心源码如下:

    @Override
    	public Authentication extract(HttpServletRequest request) {
    		String tokenValue = extractToken(request);
    		if (tokenValue != null) {
    			PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, "");
    			return authentication;
    		}
    		return null;
    	}
    
    	protected String extractToken(HttpServletRequest request) {
    		// 首先从header中解析access_token
    		String token = extractHeaderToken(request);
    
    		// 然后从request parameter中access_token
    		if (token == null) {
    			logger.debug("Token not found in headers. Trying request parameters.");
    			token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
    			if (token == null) {
    				logger.debug("Token not found in request parameters.  Not an OAuth2 request.");
    			}
    			else {
    				request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
    			}
    		}
    
    		return token;
    	}
    

    OAuth2AuthenticationManager

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            if (authentication == null) {
                throw new InvalidTokenException("Invalid token (token not found)");
            } else {
                String token = (String)authentication.getPrincipal();
                //借助tokenServices,根据token加载身份信息
                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 {
                        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);
                        return auth;
                    }
                }
            }
        }
    

    其中,最关键的tokenServices是ResourceServerTokenServices的实例。ResourceServerTokenServices接口的最主要的2个实现类是RemoteTokenServices和DefaultTokenServices。下面的断点中,我用的是远程调用,RemoteTokenServices

    下面是我部分断点的图,比较简单,就不说明了,其中pig的官网也有说明,连接:pig 校验令牌详解 【原理】 · 语雀 (yuque.com)

    image-20210822143441147

    image-20210822143747439

    image-20210822143838019

    image-20210822143917129

  • 相关阅读:
    sql-select for update
    java-JDK动态代理
    idea-热部署jreble的使用
    vue-【el-table】转成【pl-table】
    mybatis-字段值为null或为''无法存储到数据库
    vue-本地开发热部署编译缓慢
    chrome-截长图
    el-cascader 级联选择器中选中任意一级去掉圆形按钮
    idea-绿色注释颜色16进制
    markdown 语法
  • 原文地址:https://www.cnblogs.com/dalianpai/p/15172488.html
Copyright © 2011-2022 走看看