上面文章介绍了Springboot如何集成Cas的认证,但是在写代码的时候有一些疑问:
1)我们获取认证的内容的时候是SecurityContextHolder.getContext(),那么这里面的内容是什么时候放进去的,放进去的内容是什么?
2)我们自定义的userDetailService获取的结果是如何使用的,跟Authentication有什么关系?
下面带着这两个问题,跟着源码的流程往下看,源码可能跟SpringSecurity的版本有关系,我看的版本是4.2.3的
1>CasAuthenticationFilter
主要的类是CasAuthenticationFilter,他的定义如下
public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
}
首先它是继承AbstractAuthenticationProcessingFilter这个类的,我们看AbstractAuthenticationProcessingFilter这个类的filter逻辑
其实就两步,第一调用attemptAuthentication尝试认证,第二步拿到认证的结果作为参数,执行认证成功之后的流程;这两个步骤分别在子类中CasAuthenticationFilter进行具体的实现;
2>attemptAuthentication的逻辑
@Override
public Authentication attemptAuthentication(final HttpServletRequest request,
final HttpServletResponse response) throws AuthenticationException,
IOException {
// if the request is a proxy request process it and return null to indicate the
// request has been processed
if (proxyReceptorRequest(request)) {
logger.debug("Responding to proxy receptor request");
CommonUtils.readAndRespondToProxyReceptorRequest(request, response,
this.proxyGrantingTicketStorage);
return null;
}
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER
: CAS_STATELESS_IDENTIFIER;
String password = obtainArtifact(request);
if (password == null) {
logger.debug("Failed to obtain an artifact (cas ticket)");
password = "";
}
final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
这里具体的认证逻辑并没有,实现是在最后一句
this.getAuthenticationManager().authenticate(authRequest);
具体的认证逻辑在AuthenticationManager来做,具体实现是这样的
提供多个provider去认证;而对于cas来说,使用的是CasAuthenticationProvider;
3>CasAuthenticationProvider的主要逻辑
private CasAuthenticationToken authenticateNow(final Authentication authentication)
throws AuthenticationException {
try {
final Assertion assertion = this.ticketValidator.validate(authentication
.getCredentials().toString(), getServiceUrl(authentication));
final UserDetails userDetails = loadUserByAssertion(assertion);
userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails,
authentication.getCredentials(),
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()),
userDetails, assertion);
}
catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
}
这里拿ticketValidator.validate 去校验cas的ticket,校验成功之后,这个值作为CasAuthenticationToken中的一个属性,返回的结果是
CasAuthenticationToken;同时loadUserByAssertion会使用我们定义的userDetailService获取一些用户信息,也作为CasAuthenticationToken的属性;
到这里认证完成,我们接着看认证成功之后的流程,回到最开始CasAuthenticationFilter的逻辑
4>successfulAuthentication
其实重要的逻辑在这里,上面认证的结果,CasAuthenticationToken放在SecurityContext中;到此整个逻辑结束了;
上面的两个疑问也就解决了;