spring security (三)认证源码详解

首先我们通过远程接口调用Security的对外接口 (/oauth/token)

@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.");
    } else {
        String clientId = this.getClientId(principal);
        ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId);
        TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
        if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) {
            throw new InvalidClientException("Given client ID does not match authenticated client");
        } else {
            if (authenticatedClient != null) {
                this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
            }

            if (!StringUtils.hasText(tokenRequest.getGrantType())) {
                throw new InvalidRequestException("Missing grant type");
            } else if (tokenRequest.getGrantType().equals("implicit")) {
                throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
            } else {
                if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
                    this.logger.debug("Clearing scope of incoming token request");
                    tokenRequest.setScope(Collections.emptySet());
                }

                if (this.isRefreshTokenRequest(parameters)) {
                    tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope")));
                }

                //获取token的核心代码
                OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
                if (token == null) {
                    throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
                } else {
                    return this.getResponse(token);
                }
            }
        }
    }
}

首先我们看一下TokenGranter的实现类

往下走可以看到tokenGranters里边有两个对象,第一个里边放着的是springsecurity默认的五种认证方式,第二个则是我们自定义的验证码登录方式。

public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
    Iterator var3 = this.tokenGranters.iterator();

    OAuth2AccessToken grant;
    do {
        if (!var3.hasNext()) {
            return null;
        }

        TokenGranter granter = (TokenGranter)var3.next();
        grant = granter.grant(grantType, tokenRequest);
    } while(grant == null);

    return grant;
}

这里我们使用的是账号密码登录,走的第一个实现类的AuthorizationServerEndpointsConfigurer.tokenGranter

private TokenGranter tokenGranter() {
    if (this.tokenGranter == null) {
        this.tokenGranter = new TokenGranter() {
            private CompositeTokenGranter delegate;

            public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
                if (this.delegate == null) {
                    this.delegate = new CompositeTokenGranter(AuthorizationServerEndpointsConfigurer.this.getDefaultTokenGranters());
                }

                return this.delegate.grant(grantType, tokenRequest);
            }
        };
    }

    return this.tokenGranter;
}

这里的this.delegate是CompositeTokenGranter.grant,现在传进来的是我们默认的我种认证方式

我们会循环的去调用granter.grant(grantType, tokenRequest);

寻找我们跟我们传进来的grantType相同的认证方式,当然就是我们的ResourceOwnerPasswordTokenGranter

这里也会校验我们设置的哪几种认证方式,没有设置的话也是不能通过认证的;

protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
    return this.tokenServices.createAccessToken(this.getOAuth2Authentication(client, tokenRequest));
}

这里主要是this.getOAuth2Authentication(client, tokenRequest)去认证

protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
    String username = (String)parameters.get("username");
    String password = (String)parameters.get("password");
    parameters.remove("password");
    Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
    ((AbstractAuthenticationToken)userAuth).setDetails(parameters);

    Authentication userAuth;
    try {
        //认证管理器认证
        userAuth = this.authenticationManager.authenticate(userAuth);
    } catch (AccountStatusException var8) {
        throw new InvalidGrantException(var8.getMessage());
    } catch (BadCredentialsException var9) {
        throw new InvalidGrantException(var9.getMessage());
    }

    if (userAuth != null && userAuth.isAuthenticated()) {
        OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
        return new OAuth2Authentication(storedOAuth2Request, userAuth);
    } else {
        throw new InvalidGrantException("Could not authenticate user: " + username);
    }
}

authenticationManager认证管理器的主要实现类是ProviderManager,ProviderManager里边存在着账号密码的provider和我们定义的provider

@Override
public void configure(AuthenticationManagerBuilder auth) {
auth
//手机验证码
.authenticationProvider(smsCodeAuthenticationProvider())
//密码验证
.authenticationProvider(daoAuthenticationProvider());
}

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    AuthenticationException parentException = null;
    Authentication result = null;
    Authentication parentResult = null;
    boolean debug = logger.isDebugEnabled();
    Iterator var8 = this.getProviders().iterator();

    while(var8.hasNext()) {
        AuthenticationProvider provider = (AuthenticationProvider)var8.next();
        //supports校验是否支持当前的provider
        if (provider.supports(toTest)) {
            if (debug) {
                logger.debug("Authentication attempt using " + provider.getClass().getName());
            }

            try {
                //调用procider的认证方法,传参是账号密码信息
                result = provider.authenticate(authentication);
                if (result != null) {
                    this.copyDetails(authentication, result);
                    break;
                }
            } catch (InternalAuthenticationServiceException | AccountStatusException var13) {
                this.prepareException(var13, authentication);
                throw var13;
            } catch (AuthenticationException var14) {
                lastException = var14;
            }
        }
    }

    if (result == null && this.parent != null) {
        try {
            result = parentResult = this.parent.authenticate(authentication);
        } catch (ProviderNotFoundException var11) {
        } catch (AuthenticationException var12) {
            parentException = var12;
            lastException = var12;
        }
    }

    if (result != null) {
        if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
            ((CredentialsContainer)result).eraseCredentials();
        }

        if (parentResult == null) {
            this.eventPublisher.publishAuthenticationSuccess(result);
        }

        return result;
    } else {
        if (lastException == null) {
            lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
        }

        if (parentException == null) {
            this.prepareException((AuthenticationException)lastException, authentication);
        }

        throw lastException;
    }
}

这里默认的provider会调用AbstractUserDetailsAuthenticationProvider.authenticate,如果我们自己定义的就会调用我们定义的认证方法

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
        return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
    });
    String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
    boolean cacheWasUsed = true;
    UserDetails user = this.userCache.getUserFromCache(username);
    if (user == null) {
        cacheWasUsed = false;

        try {
            //数据库获取用户信息
            user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
        } catch (UsernameNotFoundException var6) {
            this.logger.debug("User '" + username + "' not found");
            if (this.hideUserNotFoundExceptions) {
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }

            throw var6;
        }

        Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
    }

    try {
        //校验用户的状态是否异常
        this.preAuthenticationChecks.check(user);
        //传进来的密码与数据库密码比较
        this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    } catch (AuthenticationException var7) {
        if (!cacheWasUsed) {
            throw var7;
        }

        cacheWasUsed = false;
        user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
        this.preAuthenticationChecks.check(user);
        this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    }

    this.postAuthenticationChecks.check(user);
    if (!cacheWasUsed) {
        this.userCache.putUserInCache(user);
    }

    Object principalToReturn = user;
    if (this.forcePrincipalAsString) {
        principalToReturn = user.getUsername();
    }

    //返回封装好的UsernamePasswordAuthenticationToken
    return this.createSuccessAuthentication(principalToReturn, authentication, user);
}

数据库获取用户信息,我们通过实现UserDetailsService类来重写loadUserByUsername方法实现查询UserDetails

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    this.prepareTimingAttackProtection();

    try {
        //
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
            return loadedUser;
        }
    } catch (UsernameNotFoundException var4) {
        this.mitigateAgainstTimingAttack(authentication);
        throw var4;
    } catch (InternalAuthenticationServiceException var5) {
        throw var5;
    } catch (Exception var6) {
        throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
    }
}

传进来的密码与数据库密码比较

protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    if (authentication.getCredentials() == null) {
        this.logger.debug("Authentication failed: no credentials provided");
        throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
    } else {
        String presentedPassword = authentication.getCredentials().toString();
        //密码比较
        if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            this.logger.debug("Authentication failed: password does not match stored value");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }
}

返回封装好的UsernamePasswordAuthenticationToken,即认证成功

protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
    UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
    result.setDetails(authentication.getDetails());
    return result;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值