记一次/oauth/token请求流程

自己本地练习spring security+oauth2进行授权,对
org.springframework.security.oauth2.provider.ClientDetailsService#loadClientByClientId方法没有做缓存处理,在请求获取token时,发现一次token请求,这个方法要被调用4次,因此跟踪一下都是哪里调用这个方法的。

请求先经过一系列过滤器,里面有个BasicAuthenticationFilter,主要用来做安全认证

org.springframework.web.filter.OncePerRequestFilter
过滤器基类,旨在确保在任何servlet容器上每个请求分派单个执行。
提供一个带有HttpServletRequest和HttpServletResponse参数的doFilterInternal方法。
org.springframework.security.web.authentication.www.BasicAuthenticationFilter#doFilterInternal
这个类继承了OncePerRequestFilter,用于安全认证时的过滤器

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        final boolean debug = this.logger.isDebugEnabled();
        try {
        //将请求转为鉴定token需要的对象
        /**
        * 1.判断请求头是否有Authorization属性,没有返回null
        * 2.判断Authorization属性值是否以Basic忽略大小写开头,不是的话返回null
        * 3.截取Authorization属性值里面的base64加密后的值,进行解码出用户名和密码
        */
            UsernamePasswordAuthenticationToken authRequest = authenticationConverter.convert(request);
            if (authRequest == null) {
                chain.doFilter(request, response);
                return;
            }
            /**
            * 获取认证用户名
            */
            String username = authRequest.getName();
            if (debug) {
                this.logger.debug("Basic Authentication Authorization header found for user '" + username + "'");
            }
            /**
            * 判断是否需要身份鉴定,true-需要鉴定
            * 1.从SecurityContext获取Authentication鉴权实例对象,如果为空或者未登陆,返回true
            * 2.如果是UsernamePasswordAuthenticationToken类型的实例对象,判断username和对象上的username是否一致,不一致返回true
            * 3.如果是AnonymousAuthenticationToken类型的实例对象,返回true
            * 4.其他情况返回false
            */
            if (authenticationIsRequired(username)) {
            	//从身份鉴定管理器获取身份鉴定对象,并放入上下文
            	/**
            	* 1.遍历找到UsernamePasswordAuthenticationToken的身份鉴定提供者AuthenticationProvider对象
            	* 2.AbstractUserDetailsAuthenticationProvider#authenticate进行认证
            	* 2-1. retrieveUser方法检索用户,调用了UserDedtailsService.loadUserByUsername,
            	* 实际实现类ClientDetailsUserDetailsService里面调用了
            	* ClientDetailsService.loadClientByClientId第1次 
            	*/
                Authentication authResult = this.authenticationManager.authenticate(authRequest);
                if (debug) {
                    this.logger.debug("Authentication success: " + authResult);
                }
                SecurityContextHolder.getContext().setAuthentication(authResult);
                this.rememberMeServices.loginSuccess(request, response, authResult);
                onSuccessfulAuthentication(request, response, authResult);
            }
        } catch (AuthenticationException failed) {
            SecurityContextHolder.clearContext();
            if (debug) {
                this.logger.debug("Authentication request for failed: " + failed);
            }
            this.rememberMeServices.loginFail(request, response);
            onUnsuccessfulAuthentication(request, response, failed);
            if (this.ignoreFailure) {
                chain.doFilter(request, response);
            } else {
                this.authenticationEntryPoint.commence(request, response, failed);
            }
            return;
        }
        chain.doFilter(request, response);
    }

完成过滤器逻辑后,进入到org.springframework.security.oauth2.provider.endpoint.TokenEndpoint#postAccessToken

@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);
		//第2次调用loadClientByClientId
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
		//这个方法里面会第3次调用loadClientByClientId
		//里面会调用DefaultOAuth2RequestFactory#extractScopes,查询scope
		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)));
		}
		//里面会第4次调用loadClientByClientId
	 	//org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant
		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}

		return getResponse(token);

	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值