前言
- 在SpringSecurity中存在多种认证方式,除了表单认证,还有记住我认证,JASS认证,基本认证,摘要认证等等
- 基本认证:是一种将用户名/密码经过Base64编码后,放在请求头的Authorization属性中,再交由服务端校验

1. HttpBasicConfigurer
- HttpBasicConfigurer作为BasicAuthenticationFilter的配置类
- 我们不要同时开启多种认证方式,ExceptionTranslationFilter中的createDefaultDeniedHandler(...)方法给出了答案
- 当我们配置了多种认证方式后就会有多个身份认证入口点(AuthenticationEntryPoint), 多个身份认证人口点又会被封装为一个委托机制的类,但是这里有一个保底的默认的身份认证人口点是取得是所有身份认证入口点的第一个,也就是defaultEntryPointMappings对象
- 而这个defaultEntryPointMappings又是一个LinkedHashMap,LinkedHashMap是HashMap的一个扩展维护一个
双向链表
,保证了顺序
- 换句话说默认的身份认证入口点是看谁先put到这个LinkedHashMap中决定的
1.1 init(...)
- init(...)很简单就是调用了registerDefaults(...)方法
- registerDefaults(...):注册参数默认值
private void registerDefaults(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
MediaType.TEXT_XML);
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
MediaTypeRequestMatcher allMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);
allMatcher.setUseEquals(true);
RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.TEXT_HTML));
RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(
Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));
registerDefaultEntryPoint(http, preferredMatcher);
registerDefaultLogoutSuccessHandler(http, preferredMatcher);
}
1.2 configure(...)
public void configure(B http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,
this.authenticationEntryPoint);
if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
}
basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
http.addFilter(basicAuthenticationFilter);
}
2. BasicAuthenticationFilter
- doFilterInternal(...):和UsernamePasswordAuthenticationFilter一样都是借助AuthenticationManager进行认证的
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
if (authRequest == null) {
this.logger.trace("Did not process authentication request since failed to find "
+ "username and password in Basic Authorization header");
chain.doFilter(request, response);
return;
}
String username = authRequest.getName();
this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
if (authenticationIsRequired(username)) {
Authentication authResult = this.authenticationManager.authenticate(authRequest);
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
}
this.rememberMeServices.loginSuccess(request, response, authResult);
onSuccessfulAuthentication(request, response, authResult);
}
}
catch (AuthenticationException ex) {
SecurityContextHolder.clearContext();
this.logger.debug("Failed to process authentication request", ex);
this.rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response, ex);
if (this.ignoreFailure) {
chain.doFilter(request, response);
}
else {
this.authenticationEntryPoint.commence(request, response, ex);
}
return;
}
chain.doFilter(request, response);
}
- convert(...):通过解析request中的某些参数转为认证对象
public UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header == null) {
return null;
}
header = header.trim();
if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {
return null;
}
if (header.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {
throw new BadCredentialsException("Empty basic authentication token");
}
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded = decode(base64Token);
String token = new String(decoded, getCredentialsCharset(request));
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
}
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(token.substring(0, delim),
token.substring(delim + 1));
result.setDetails(this.authenticationDetailsSource.buildDetails(request));
return result;
}
- authenticationIsRequired(...):确定是否需要认证
private boolean authenticationIsRequired(String username) {
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
return (existingAuth instanceof AnonymousAuthenticationToken);
}