springBoot2.X+spring security5.3.8+redis整合用户登录权限控制

  • 主要功能点介绍
  • 重要代码片段


主要功能点介绍

1.权限过滤
2.改造attemptAuthentication,重写该方法目的在于支持JSON格式提交登录信息。
3.验证后的返回信息均实现相应接口重写相应方法,以JSON格式返回前端。
4.会话管理,session并发控制过滤器,限制同一账号同时登录数量。
5.针对集群部署场景,通过redis实现session共享。

重要代码片段

package com.hexy.hexy_umagt.config;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;

import com.hexy.hexy_umagt.config.security.AnonymousAuthenticationEntryPoint;
import com.hexy.hexy_umagt.config.security.CustomConcurrentSessionFilter;
import com.hexy.hexy_umagt.config.security.DefaultUserDetailsService;
import com.hexy.hexy_umagt.config.security.InvalidSessionHandler;
import com.hexy.hexy_umagt.config.security.LoginFailureHandler;
import com.hexy.hexy_umagt.config.security.LoginSuccessHandler;
import com.hexy.hexy_umagt.config.security.LoginUserAccessDeniedHandler;
import com.hexy.hexy_umagt.config.security.LogoutSuccessHandler;
import com.hexy.hexy_umagt.config.security.MyAuthenticationProvider;
import com.hexy.hexy_umagt.config.security.MyConcurrentSessionControlAuthenticationStrategy;
import com.hexy.hexy_umagt.config.security.MyUsernamePasswordAuthenticationFilter;
import com.hexy.hexy_umagt.config.security.RestHttpSessionIdResolver;
import com.hexy.hexy_umagt.config.security.SessionInformationExpiredHandler;

/**
 * 支持JSON登录提交,返回json数据(同时支持表单提交验证)
 * session并发控制,只允许一个用户同时在线。
 * @author hxy
 *
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DefaultUserDetailsService userDetailsService;
    /**
     * 登出成功的处理
     */
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    /**
     * 登录成功的处理
     */
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    /**
     * 登出成功的处理
     */
    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;
    /**
     * 未登录的处理
     */
    @Autowired
    private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
    /**
     * 超时处理
     */
    @Autowired
    private InvalidSessionHandler invalidSessionHandler;
    /**
     * 顶号处理
     */
    @Autowired
    private SessionInformationExpiredHandler sessionInformationExpiredHandler;
    
    /**
     * 登录用户没有权限访问资源
     */
    @Autowired
    private LoginUserAccessDeniedHandler accessDeniedHandler;

    @Autowired
    private MyAuthenticationProvider authenticationProvider;
    
    @Resource
    private SessionRegistry sessionRegistry;
    /**
     * 配置认证方式等
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(authenticationProvider);
    }

    /**
     * http相关的配置,包括登入登出、异常处理、会话管理等
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        http.authorizeRequests()
                // 放行接口
                .antMatchers("/register","/login").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                // 异常处理(权限拒绝、登录失效等)
                .and().exceptionHandling()
                .authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
                .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
                // 登入
                .and().formLogin().permitAll()//允许所有用户
//                .successHandler(loginSuccessHandler)//登录成功处理逻辑
//                .failureHandler(loginFailureHandler)//登录失败处理逻辑
                // 登出
                .and().logout().permitAll()//允许所有用户
                .logoutSuccessHandler(logoutSuccessHandler)//登出成功处理逻辑
                .deleteCookies(RestHttpSessionIdResolver.AUTH_TOKEN)
                // 会话管理
                .and().sessionManagement();
//                .sessionAuthenticationStrategy(concurrentSession());
//                .invalidSessionStrategy(invalidSessionHandler) // 超时处理
//                .maximumSessions(1)//同一账号同时登录最大用户数
//                .expiredSessionStrategy(sessionInformationExpiredHandler); // 顶号处理;

        http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        //session并发控制过滤器
        http.addFilterAt(concurrentSessionFilter(), ConcurrentSessionFilter.class);
        
    }

  //在全局中注入Bean:(Spring Security 提供了BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强)
  @Bean
  public BCryptPasswordEncoder bcryptPasswordEncoder(){
  	return new BCryptPasswordEncoder();
  }
  

  @Bean
  public SessionRegistry sessionRegistry() {
      return new SessionRegistryImpl();
  }

  //SpringSecurity内置的session监听器
  @Bean
  public HttpSessionEventPublisher httpSessionEventPublisher() {
      return new HttpSessionEventPublisher();
  }
  
  @Bean()
  MyUsernamePasswordAuthenticationFilter  myAuthenticationFilter() throws Exception {
	
	  //自定义登录验证获取用户名密码方式兼容JSON及表单提交
	  MyUsernamePasswordAuthenticationFilter  filter = new MyUsernamePasswordAuthenticationFilter();

	  //自定义json上送登录信息时,重新登录方法改变默认的getParameter获取,解析json获取,同时重定向登录成功需要重新set
	  filter.setAuthenticationSuccessHandler(loginSuccessHandler);
	  filter.setAuthenticationFailureHandler(loginFailureHandler);

      filter.setAuthenticationManager(authenticationManager());
     
      //session并发控制,因为默认的并发控制方法是空方法.这里必须自己配置一个
      //不可这么写----》filter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry()));
      //错误点,这里一定要引用自己重写的session(否则后面是取不到principals的,无论如何都是null)
      filter.setSessionAuthenticationStrategy(concurrentSession());
      return filter;
  }
  
  @Bean
  @Override
  protected AuthenticationManager authenticationManager() throws Exception {
      return super.authenticationManager();
  }
  
  
  @Bean
  public CompositeSessionAuthenticationStrategy concurrentSession() {

//           ConcurrentSessionControlAuthenticationStrategy concurrentAuthenticationStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
	  		MyConcurrentSessionControlAuthenticationStrategy concurrentAuthenticationStrategy = new MyConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
           concurrentAuthenticationStrategy.setMaximumSessions(1);
//           concurrentAuthenticationStrategy.setExceptionIfMaximumExceeded(true);
           List<SessionAuthenticationStrategy> delegateStrategies = new ArrayList<SessionAuthenticationStrategy>();
           delegateStrategies.add(concurrentAuthenticationStrategy);
           delegateStrategies.add(new SessionFixationProtectionStrategy());
           delegateStrategies.add(new RegisterSessionAuthenticationStrategy(sessionRegistry()));

           CompositeSessionAuthenticationStrategy authenticationStrategy =  new CompositeSessionAuthenticationStrategy(delegateStrategies);
           return authenticationStrategy;
   }

   @Bean()
   ConcurrentSessionFilter concurrentSessionFilter() {
       CustomConcurrentSessionFilter concurrentSessionFilter = new CustomConcurrentSessionFilter(sessionRegistry(), sessionInformationExpiredHandler);
       return concurrentSessionFilter;
   }
}

以上片段仅展示所有功能点,具体重写内容可通过git了解。代码均已上传,且已验证过。

session共享使用redis主要要启动注解

@Slf4j
@EnableTransactionManagement
@MapperScan("com.hexy.hexy_umagt.mapper")
@SpringBootApplication
@EnableDubbo
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)//session共享redis实现
public class HexyUmagtApplication {

	public static void main(String[] args) {
		SpringApplication.run(HexyUmagtApplication.class, args);
	}
}

所有重写的实现类就不直接贴出来,详情见GIt吧。
GIT地址:https://gitee.com/hellohexiaoyu/commonframework

最后说明下整个security框架其实还是比较繁琐的,不注意时会遇到很多棘手问题,网络上有很多博客,但是提供的内容不一定适用自己,所以遇到问题还是要多看源码,先理解整个框架运行原理。剩下的问题基本都会迎刃而解。