目录
- 1. 认证步骤
- 2. 认证
- 2.1 WebSecurityConfigurerAdapter配置介绍
- 2.2 使用UsernamePasswordAuthenticationFilter登录认证
- 2.2.1 UsernamePasswordAuthenticationFilter源码
- 2.2.1.1 ProviderManager源码
- 2.2.2 认证流程总结
- 2.3 自定义登录认证代码示例
- 2.3.1 认证流程总结
1. 认证步骤
Spring Security实现认证的主要步骤如下:
-
配置用户存储:您可以选择将用户信息存储在内存中、数据库中或其他外部身份验证源中。通过配置UserDetailsService或AuthenticationProvider,Spring Security可以获取用户的凭据和权限信息。
-
用户认证:当用户尝试登录时,Spring Security会验证用户提供的凭据(例如用户名和密码)。它使用AuthenticationManager来处理认证过程。AuthenticationManager会调用配置的AuthenticationProvider来验证用户凭据的有效性。
-
身份验证过滤器:Spring Security使用身份验证过滤器来拦截登录请求并进行身份验证。通常使用UsernamePasswordAuthenticationFilter来处理基于用户名和密码的认证请求。该过滤器会验证用户提供的凭据,并将认证结果封装成一个Authentication对象。
-
认证管理器:AuthenticationManager是Spring Security的核心接口之一,用于管理和执行身份验证过程。它负责调用配置的AuthenticationProvider来验证用户凭据的有效性。
-
认证成功处理器:当认证成功时,可以配置一个认证成功处理器来处理成功的认证请求。该处理器可以执行一些自定义逻辑,例如生成和返回访问令牌或重定向到特定页面。
-
认证失败处理器:当认证失败时,可以配置一个认证失败处理器来处理失败的认证请求。该处理器可以返回错误消息或重定向到登录页面等。
2. 认证
2.1 WebSecurityConfigurerAdapter配置介绍
WebSecurityConfigurerAdapter是Spring Security提供的一个方便的基类,用于自定义安全配置。通过继承WebSecurityConfigurerAdapter类,并重写其中的方法,可以实现对Spring Security的自定义配置。 通过对configure方法的重新,我们可以做很多配置。
下面是configure方法中常用配置的含义和对应的代码示例(Java):
- authorizeRequests(): 配置URL的访问权限规则。可以通过antMatchers()指定URL模式,通过permitAll()允许所有用户访问,通过authenticated()要求进行身份验证。
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/public").permitAll().anyRequest().authenticated();}
- formLogin(): 配置表单登录相关的设置,包括登录页面、登录请求的URL、登录成功和失败的处理等。
protected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login").loginProcessingUrl("/authenticate").defaultSuccessUrl("/home").failureUrl("/login?error=true");}
- logout(): 配置登出相关的设置,包括登出URL、登出成功的处理等。
protected void configure(HttpSecurity http) throws Exception {http.logout().logoutUrl("/logout").logoutSuccessUrl("/login?logout=true");}
- antMatchers(): 配置URL模式和对应的访问权限。
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/public").permitAll().antMatchers("/admin").hasRole("ADMIN").anyRequest().authenticated();}
- permitAll(): 允许所有用户访问指定的URL。
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/public").permitAll();}
- authenticated(): 要求用户进行身份验证才能访问指定的URL。
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated();}
- 设置自定义过滤器,用于token校验,并把自定义的过滤器加入到过滤器链。我们用addFilterBefore()做例子,还有其他的api方法可以使用
@Component
public class CustomFilter implements GenericFilterBean{@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 过滤器逻辑,在此可以做接口的token校验chain.doFilter(request, response);}
}//把自定义的过滤器加入到过滤器链中
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomFilter customFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class).authorizeRequests().antMatchers("/public").permitAll().anyRequest().authenticated().and().formLogin().and().logout();}
}
- 自定义accessDeniedHandler和authenticationEntryPoint
accessDeniedHandler是对授权失败的处理接口;authenticationEntryPoint是对认证失败的处理。
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,org.springframework.security.access.AccessDeniedException accessDeniedException)throws IOException, ServletException {// 在这里自定义处理访问被拒绝的情况response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");}
}@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {// 在这里自定义处理未经身份验证的情况response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");}
}public class SecurityConfig extends WebSecurityConfigurerAdapter {private final AccessDeniedHandler accessDeniedHandler;private final AuthenticationEntryPoint authenticationEntryPoint;@Autowiredpublic SecurityConfig(CustomAccessDeniedHandler accessDeniedHandler,CustomAuthenticationEntryPoint authenticationEntryPoint) {this.accessDeniedHandler = accessDeniedHandler;this.authenticationEntryPoint = authenticationEntryPoint;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.exceptionHandling().accessDeniedHandler(accessDeniedHandler) // 设置自定义的accessDeniedHandler.authenticationEntryPoint(authenticationEntryPoint) // 设置自定义的authenticationEntryPoint.and()// 其他的配置...}
}
这些只是一些常用的配置示例,实际使用中可能需要根据需求进行更复杂的配置和自定义。
2.2 使用UsernamePasswordAuthenticationFilter登录认证
下面是使用UsernamePasswordAuthenticationFilter进行登录认证,并将账号密码存储在数据库中的Java代码示例:
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username = ?").authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username = ?").passwordEncoder(passwordEncoder());}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic UsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();filter.setAuthenticationManager(authenticationManagerBean());filter.setFilterProcessesUrl("/login");return filter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class).authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();}
}
在上面的代码中,我们首先配置了一个DataSource,用于连接数据库。然后在SecurityConfig类中,通过configure方法配置了HttpSecurity,定义了登录页面、注销页面,以及哪些请求需要进行身份验证。接着,我们通过configure方法配置了AuthenticationManagerBuilder,使用jdbcAuthentication()方法来指定使用数据库进行身份验证,并提供了查询用户信息和权限的SQL语句。我们还使用了passwordEncoder()方法来指定密码的加密方式,这里使用了BCryptPasswordEncoder。最后,我们通过@Bean注解定义了一个UsernamePasswordAuthenticationFilter,并将其添加到了过滤器链中,用于拦截登录请求并进行身份验证。
这只是简单的代码示例,主要是为了理解原理,我们真正使用的时候还是要自定义一个登录过滤器和token校验过滤器。下一章我们会举一个前后端分离,自定义登录认证的代码示例。
2.2.1 UsernamePasswordAuthenticationFilter源码
UsernamePasswordAuthenticationFilter是Spring Security框架中的一个过滤器,用于处理基于用户名和密码的身份验证。它是Spring Security核心过滤器链中的一部分,负责拦截用户的登录请求,并将用户名和密码交给AuthenticationManager进行身份验证处理。如果身份验证成功,该过滤器会创建一个包含用户信息和权限的认证对象,并将其交给SecurityContextHolder进行管理。如果身份验证失败,则会返回错误信息给用户。通过该过滤器,可以实现基于表单的登录认证
*/
public class UsernamePasswordAuthenticationFilter extends org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter {public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";//此过滤器拦截的接口private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST");private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;private boolean postOnly = true;//无参构造器,使用默认的AuthenticationManager实现public UsernamePasswordAuthenticationFilter() {super(DEFAULT_ANT_PATH_REQUEST_MATCHER);}//有参构造器,使用方可自定义AuthenticationManager,通过实现AuthenticationManager接口public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);}
可以看到此过滤器拦截自带的/login接口
UsernamePasswordAuthenticationFilter继承自AbstractAuthenticationProcessingFilter,
UsernamePasswordAuthenticationFilter没有重写doFilter()方法,我们看AbstractAuthenticationProcessingFilter的doFilter()方法。
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}try {//attemptAuthentication是抽象方法,可被子类重写Authentication authenticationResult = attemptAuthentication(request, response);if (authenticationResult == null) {// return immediately as subclass has indicated that it hasn't completedreturn;}//成功后会this.sessionStrategy.onAuthentication(authenticationResult, request, response);// Authentication successif (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}//保存用户信息等successfulAuthentication(request, response, chain, authenticationResult);}catch (InternalAuthenticationServiceException failed) {this.logger.error("An internal error occurred while trying to authenticate the user.", failed);unsuccessfulAuthentication(request, response, failed);}catch (AuthenticationException ex) {// Authentication failedunsuccessfulAuthentication(request, response, ex);}}
认证逻辑主要在attemptAuthentication,attemptAuthentication是抽象方法,被子类UsernamePasswordAuthenticationFilter覆写,我们看下覆写逻辑。
//将request中的参数提取出来保存到 Authentication 中, 返回给认证器认证, 就这么一步, 如果你是前后端分离, 就有可能需要重写该方法@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//请求中获取用户名String username = obtainUsername(request);username = (username != null) ? username : "";username = username.trim();//从请求中获取密码String password = obtainPassword(request);password = (password != null) ? password : "";//构建个UsernamePasswordAuthenticationToken令牌,继承自AuthenticationUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);//把令牌传入AuthenticationManager,进行认证return this.getAuthenticationManager().authenticate(authRequest);}
主要逻辑是this.getAuthenticationManager().authenticate(authRequest);可以看到是把UsernamePasswordAuthenticationToken从传入到AuthenticationManager。AuthenticationManager的接口的主要实现类是ProviderManager,核心认证逻辑就在ProviderManager类中。
2.2.1.1 ProviderManager源码
我们看下ProviderManager类的authenticate()方法,代码过长,主要看下主要逻辑。
//认证器集合private List<org.springframework.security.authentication.AuthenticationProvider> providers = Collections.emptyList();@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;int currentPosition = 0;int size = this.providers.size();//支持多种认证,遍历所有AuthenticationProviderfor (org.springframework.security.authentication.AuthenticationProvider provider : getProviders()) {//匹配当前的Authenticationif (!provider.supports(toTest)) {continue;}if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",provider.getClass().getSimpleName(), ++currentPosition, size));}try {//执行匹配到的AuthenticationProvider逻辑result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}}
我们根据supports()方法,匹配到正确的AuthenticationProvider是DaoAuthenticationProvider。DaoAuthenticationProvider继承AbstractUserDetailsAuthenticationProvider。
核心逻辑:
//校验用户密码并返回用户信息@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));String username = determineUsername(authentication);boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {//返回本地用户信息user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException ex) {this.logger.debug("Failed to find user '" + username + "'");if (!this.hideUserNotFoundExceptions) {throw ex;}throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");}try {this.preAuthenticationChecks.check(user);//请求密码和本地密码匹配additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}
DaoAuthenticationProvider中覆写的方法。
//从缓存或者数据库查询用户信息@Overrideprotected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {prepareTimingAttackProtection();try {//从缓存或者数据库查询用户信息//默认spring security保存在内存中, 如果你需要改从数据库中拿到用户, 就需要重写UserDetailsServiceUserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;} catch (UsernameNotFoundException ex) {mitigateAgainstTimingAttack(authentication);throw ex;} catch (InternalAuthenticationServiceException ex) {throw ex;} catch (Exception ex) {throw new InternalAuthenticationServiceException(ex.getMessage(), ex);}}
//密码验证方法@Override@SuppressWarnings("deprecation")protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials() == null) {this.logger.debug("Failed to authenticate since no credentials provided");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}String presentedPassword = authentication.getCredentials().toString();//比较密码是否相同if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug("Failed to authenticate since password does not match stored value");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}}
可以看到,这段逻辑是:比较用户输入的账号密码和数据库或缓存中的用户密码是否匹配。如果匹配成功,认证通过,存储用户信息到上下文中,发送监听事件。认证失败,报错返回前端。
2.2.2 认证流程总结
Spring Security认证流程总结如下:
- 用户发起登录请求,提交用户名和密码。
- UsernamePasswordAuthenticationFilter拦截登录请求,获取到用户名和密码。
- UsernamePasswordAuthenticationFilter将用户名和密码封装成一个Authentication对象。
- AuthenticationManager负责对Authentication对象进行身份验证。
- AuthenticationManager选择合适的AuthenticationProvider进行身份验证,通常使用DaoAuthenticationProvider。
- DaoAuthenticationProvider使用UserDetailsService从数据库或其他数据源中获取用户信息。
- DaoAuthenticationProvider使用PasswordEncoder对输入的密码进行加密,然后与数据库中的密码进行比对。
- 如果密码匹配成功,DaoAuthenticationProvider将创建一个包含用户信息和权限的认证对象。
- 认证对象将被存储在SecurityContextHolder中,以便在整个请求过程中进行访问控制。
- 如果密码匹配失败,DaoAuthenticationProvider将抛出异常,登录失败。
- 认证成功后,用户将被重定向到登录成功的页面,或者继续访问原始请求的页面。
这是一个简化的Spring Security认证流程,具体的流程可能会根据配置和需求有所不同。但总体来说,Spring Security提供了一个灵活且可定制的认证框架,可以满足各种身份验证需求。
2.3 自定义登录认证代码示例
- 创建一个名为 User 的实体类,表示用户信息,包含用户名、密码和角色等属性。
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;private String role;// 构造函数、getter和setter方法省略
}
- 创建一个名为 UserRepository 的接口,用于操作用户信息的数据库存储。
public interface UserRepository {//查询数据库用户信息,接口实现类自定义编写User findByUsername(String username);
}
- 创建一个名为 UserDetailsServiceImpl 的类,实现Spring Security的 UserDetailsService 接口,用于从数据库中加载用户信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {private final UserRepository userRepository;@Autowiredpublic UserDetailsServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userRepository.findByUsername(username);if (user == null) {throw new UsernameNotFoundException("User not found");}return org.springframework.security.core.userdetails.User.withUsername(user.getUsername()).password(user.getPassword()).roles(user.getRole()).build();}
}
返回对象是org.springframework.security.core.userdetails.User,这是springSecurity内部对象,如果不满足我们可以自定义返回对象,自定义返回对象要继承springSecurity的UserDetails。
- 创建一个名为 SecurityConfig 的配置类,继承自 WebSecurityConfigurerAdapter ,用于配置Spring Security。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final UserDetailsServiceImpl userDetailsService;@Autowiredpublic SecurityConfig(UserDetailsServiceImpl userDetailsService) {this.userDetailsService = userDetailsService;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN") // 需要ADMIN角色才能访问.antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 需要ADMIN或USER角色才能访问.anyRequest().authenticated().and().formLogin().and().logout().logoutSuccessUrl("/").and().csrf().disable();}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/resources/**");}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}//密码加密方法@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}
可以根据需求修改configure()方法。
- 创建一个名为 UserController 的控制器类,用于处理登录请求。
@RestController
public class UserController {private final UserRepository userRepository;private final PasswordEncoder passwordEncoder;private final AuthenticationManager authenticationManager;@Autowiredpublic UserController(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager) {this.userRepository = userRepository;this.passwordEncoder = passwordEncoder;this.authenticationManager = authenticationManager;}@PostMapping("/login")public String login(@RequestBody User user) {User storedUser = userRepository.findByUsername(user.getUsername());if (storedUser != null && passwordEncoder.matches(user.getPassword(), storedUser.getPassword())) {Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));SecurityContextHolder.getContext().setAuthentication(authentication);return "登录成功";} else {return "用户名或密码错误";}}
}
- 设置自定义过滤器,用于token校验,并把自定义的过滤器加入到过滤器链。我们用addFilterBefore()做例子,还有其他的api方法可以使用
@Component
public class CustomFilter implements GenericFilterBean{@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 过滤器逻辑,在此可以做接口的token校验chain.doFilter(request, response);}
}//把自定义的过滤器加入到过滤器链中
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomFilter customFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class).authorizeRequests().antMatchers("/public").permitAll().anyRequest().authenticated().and().formLogin().and().logout();}
}
UsernamePasswordAuthenticationToken 是一个基于用户名和密码的身份验证令牌。
此令牌作为参数传入AuthenticationManager。在AuthenticationManager中,和2步骤中我们自定义的 loadUserByUsername()方法返回的用户信息做匹配,匹配成功认证成功,把认证信息设置到SecurityContextHolder中,失败返回错误信息。
其他接口请求后端接口的时候,会通过我们的自定义过滤器CustomFilter 校验token。
以上只是简单的代码示例,主要是帮助我们理解认证流程。真正的项目代码要更完善,细节点与功能点要更多,也会用到其他框架的东西,比如JWT。
2.3.1 认证流程总结
Spring Security认证流程总结如下:
- 用户发起登录请求,提交用户名和密码,根据用户名和密码封装成一个Authentication对象。
- AuthenticationManager负责对Authentication对象进行身份验证。
- AuthenticationManager选择合适的AuthenticationProvider进行身份验证,通常使用DaoAuthenticationProvider。
- DaoAuthenticationProvider使用UserDetailsService从数据库或其他数据源中获取用户信息。
- DaoAuthenticationProvider使用PasswordEncoder对输入的密码进行加密,然后与数据库中的密码进行比对。
- 如果密码匹配成功,DaoAuthenticationProvider将创建一个包含用户信息和权限的认证对象。
- 认证对象将被存储在SecurityContextHolder中,以便在整个请求过程中进行访问控制。 登录接口返货token给调用方,方便后续接口调用。
- 如果密码匹配失败,DaoAuthenticationProvider将抛出异常,登录失败。
- 认证成功后,用户将被重定向到登录成功的页面,或者继续访问原始请求的页面。
10.其他页面访问, 校验token是否有效。有效正常访问接口,无效返回错误,让用户登录。