首先我们通过远程接口调用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;
}