看清spring security 认证流程,自定义认证方式

一、文献参考

Spring Security认证与授权的原理(源码分析,超详细)_Zystem-CSDN博客_springsecurity认证原理

spring security为什么这么复杂? - 知乎

最简单易懂的Spring Security 身份认证流程讲解 - 曾俊杰的专栏 - 博客园

SpringSecurity认证流程源码详解 - 简书

二、认证流程

2.1、AbstractAuthenticationProcessingFilter 用户名密码表单登录过滤器

是处理表单登陆的过滤器,与 表单登陆有关的所有操作都是在该类中及其子类中进行的。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;

   if (!requiresAuthentication(request, response)) {
      chain.doFilter(request, response);

      return;
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Request is to process authentication");
   }

   Authentication authResult;

   try {
      authResult = attemptAuthentication(request, response);//在此处调用 的是`UsernamePasswordAuthenticationFilter`实现的方法。
      if (authResult == null) {
         // return immediately as subclass has indicated that it hasn't completed
         // authentication
         return;
      }
      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);//验证成功后调用此方法
}
protected void successfulAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, Authentication authResult)
      throws IOException, ServletException {

   if (logger.isDebugEnabled()) {
      logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
            + authResult);
   }

   SecurityContextHolder.getContext().setAuthentication(authResult);//将返回的用户对象放置到全局对象SecurityContext中

   rememberMeServices.loginSuccess(request, response, authResult);

   // Fire event
   if (this.eventPublisher != null) {
      eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
            authResult, this.getClass()));
   }

   successHandler.onAuthenticationSuccess(request, response, authResult);
}

在验证成功后上面这个方法会将返回的Authentication放置到SecurityContext中,我们可以通过 SecurityContextHolder.getContext().getAuthentication()获取用户信息。

2.2、UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 继承自AbstractAuthenticationProcessingFilter

public UsernamePasswordAuthenticationFilter() {
   super(new AntPathRequestMatcher("/login", "POST"));
}

用于拦截/login路径且为post的请求。其他请求不会走UsernamePasswordAuthenticationFilter拦截器。 此处的/login路径也可以进行修改。

public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) throws AuthenticationException {
   if (postOnly && !request.getMethod().equals("POST")) {
      throw new AuthenticationServiceException(
            "Authentication method not supported: " + request.getMethod());
   }

   String username = obtainUsername(request);//从request中获取用户名,
   String password = obtainPassword(request);//从request中国获取密码。

   if (username == null) {
      username = "";
   }

   if (password == null) {
      password = "";
   }

   username = username.trim();

   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
         username, password);//将用户名和密码封装程一个UsernamePasswordAuthenticationToken

   // Allow subclasses to set the "details" property
   setDetails(request, authRequest);

   return this.getAuthenticationManager().authenticate(authRequest);//将封装好的UsernamePasswordAuthenticationToken交给AuthenticationManager去认证
}

attemptAuthentication方法实现父类的方法。用于具体的表单操作。

2.3、UsernamePasswordAuthenticationFilter是什么时候被加入到拦截器链中的呢?

protected void configure(HttpSecurity http) throws Exception {
   http.formLogin()
                .loginPage("/login/goLogin")   // 指定登录页面(当我们未登录状态访问一个页面时会自动跳转到此处指定的页面)
                .loginProcessingUrl("/my2/login")   // 指定登录url(默认为/login,此处和表单的action要一致,即登录信息提交到此处指定的url后security才可进行登录处理)
                .usernameParameter("username")  // 指定登录用户名参数名称(默认为username)
                .passwordParameter("password")  // 指定密码参数名称(默认为password)
                .successHandler(new LoginSuccessHandler(dataSource)) //登录成功后处理类
//                .defaultSuccessUrl("/login/noPermit")  // 指定登录成功后跳转页 可以在此接口处做一些其他的操作比如进行认证获取token,successHandler与defaultSuccessUrl只可选择其中一个方式
                .failureUrl("/other/loginError") // 指定登录失败URL,可在该接口中抛出异常。(默认为/login?error,就是在controller中写一个接口,登录失败会跳转那个接口)
                .permitAll();
}

这个方放不陌生吧。这是我们自己继承WebSecurityConfigurerAdapter并且重写的一个方法。在此方法中我们可以配置登录相关的东西。 http.formLogin()返回的是一个FormLoginConfigurer对象。

public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
   return getOrApply(new FormLoginConfigurer<>());
}

在formLogin()方法中new了一个FormLoginConfigurer对象。接下来往下看FormLoginConfigurer类的构造方法

public FormLoginConfigurer() {
		super(new UsernamePasswordAuthenticationFilter(), null);
		usernameParameter("username");
		passwordParameter("password");
	}

 在这个构造方法中调用了父类的有参构造方法,并且传入了一个UsernamePasswordAuthenticationFilter过滤器。此处便是添加UsernamePasswordAuthenticationFilter过滤器的关键。再次进入父类的构造方法中

protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
			String defaultLoginProcessingUrl) {
		this();
		this.authFilter = authenticationFilter;
		if (defaultLoginProcessingUrl != null) {
			loginProcessingUrl(defaultLoginProcessingUrl);
		}
	}

将UsernamePasswordAuthenticationFilter对象传递进来了

在configure(HttpSecurity http)中http.formLogin()的配置都会传值到AbstractAuthenticationFilterConfigurer对象中。也就是说。当你调用http.formLogin()时UsernamePasswordAuthenticationFilter就会被当成一个拦截器加入到拦截器链中。

2.4、attemptAuthentication方法中的this.getAuthenticationManager().authenticate(authRequest);是怎么进行认证的?

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
      InitializingBean {

   private static final Log logger = LogFactory.getLog(ProviderManager.class);

   private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
   private List<AuthenticationProvider> providers = Collections.emptyList();
   protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
   private AuthenticationManager parent;
   private boolean eraseCredentialsAfterAuthentication = true;

   public ProviderManager(List<AuthenticationProvider> providers) {
      this(providers, null);
   }

   public ProviderManager(List<AuthenticationProvider> providers,
         AuthenticationManager parent) {
      Assert.notNull(providers, 
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security是一个基于Spring框架的安全框架,它提供了一套完整的安全认证和授权机制。下面是Spring Security认证流程: 1. 用户访问需要认证的资源,Spring Security会拦截请求并重定向到登录页面。 2. 用户输入用户名和密码,提交表单。 3. Spring Security会将表单提交的用户名和密码封装成一个Authentication对象。 4. AuthenticationManager接口会根据Authentication对象中的用户名和密码去调用UserDetailsService接口的实现类获取用户信息。 5. 如果获取到用户信息,则将用户信息封装成一个包含权限信息的Authentication对象返回给AuthenticationManager。 6. AuthenticationManager会将Authentication对象交给AuthenticationProvider接口的实现类进行认证。 7. 如果认证成功,则将认证成功的Authentication对象返回给Spring Security。 8. Spring Security会将认证成功的Authentication对象存储到SecurityContextHolder中,供后续的访问授权使用。 下面是一个简单的Spring Security认证流程的代码示例: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") .anyRequest().authenticated() .and() .formLogin() .and() .logout().logoutSuccessUrl("/login").permitAll(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值