10、spring security拦截器之UsernamePasswordAuthenticationFilter
程序员文章站
2024-03-19 15:33:04
...
UsernamePasswordAuthenticationFilter:登录验证
在SecurityConfig中对应:
http.formLogin() //表单登录
.loginPage("/login.html") //登录页面
.loginProcessingUrl("/login") //登录的url,默认:/login
.usernameParameter("username") //用户名参数名称,默认:username
.passwordParameter("password") //密码参数名称,默认:password
源码
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//1、验证是否是登录url,默认值:/login
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
//2、获取认证结果Authentication
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
//3、新的身份验证发生时的操作,可以对同时登录相同账号的用户进行限制
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
attemptAuthentication():获取认证结果Authentication
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
//1、获取用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//2、对请求进行验证,AuthenticationManager的实现类是ProviderManager
return this.getAuthenticationManager().authenticate(authRequest);
}
ProviderManager.authenticate(Authentication)
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();
//1、获取认证提供者AuthenticationProvider
//这里获取的值只有AnonymousAuthenticationProvider
for (AuthenticationProvider provider : getProviders()) {
//2、由于AnonymousAuthenticationProvider不支持处理这个Authentication
//继续下一个循环
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
//2.1 如果支持,则调用认证方法
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
//3、如果上面的认证结果为空,并且parent不为空,则调用parent.authenticate()
if (result == null && parent != null) {
// Allow the parent to try.
try {
/**
这里和上面唯一的区别就是getProviders()获取的值不一样,
parent里面的AuthenticationProvider实现类有DaoAuthenticationProvider,
内部会调用UserDetailsService.loadUserByUsername()方法根据用户名加载用户,
我们之前编写的CustomUserDetailsService类就是在这里被调用的
*/
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
if (parentException == null) {
prepareException(lastException, authentication);
}
throw lastException;
}
parent.authenticate(Authentication):这里和上面唯一的区别就是getProviders()获取的值不一样,parent里面的AuthenticationProvider实现类有DaoAuthenticationProvider,内部会调用UserDetailsService.loadUserByUsername()方法根据用户名加载用户,我们之前编写的CustomUserDetailsService类就是在这里被调用的
推荐阅读
-
10、spring security拦截器之UsernamePasswordAuthenticationFilter
-
9、spring security拦截器之LogoutFilter
-
Spring Boot 2.2.6 源码之旅三十五SpringMVC源码细节之拦截器
-
spring-security(三)java config-sample之hello world 博客分类: spring security springsecurity
-
spring-security(四)java config-sample之jdbc 博客分类: spring security securityspring
-
Spring Boot(四)之使用JWT和Spring Security保护REST API
-
Spring Boot(四)之使用JWT和Spring Security保护REST API
-
详解spring security之httpSecurity使用示例
-
详解spring security之httpSecurity使用示例
-
Spring Boot项目实战之拦截器与过滤器