2023.7.22 SpringSercurity Session登录态 数据校验

新版本SpringSercurity配置AuthenticationManager
  • WebSecurityConfigurerAdapter在新版本已经过时,不能再通过重写方法来获取AuthenticationManager对象
  • 新版本中直接传参数new出来个AuthenticationManager对象即可
@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Resource
    private UserDetailsService userDetailsService; //实现好UserDetailsService
    
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());

        return new ProviderManager(authenticationProvider);
    }
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private SchoolManagerService schoolManagerService;

    @Resource
    private RoleService roleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<SchoolManager> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        SchoolManager schoolManager = schoolManagerService.getOne(queryWrapper);

        ThrowUtils.throwIf(schoolManager == null, ErrorCode.PARAMS_ERROR, "用户名不存在");

        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(schoolManager, loginUser);

        //设置权限列表
        List<String> permissionList = roleService.getPermissionByRoleId(schoolManager.getRoleId());
       								loginUser.setAuthorities(permissionList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));

        return loginUser;
    }
}
新版本SpringSercurity设置白名单
  • 这里形参的http会报错,不管他就行
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 设置白名单,注意不要加/api
                .antMatchers("/doc.html",
                        "/favicon.ico",
                        "/v2/api-docs",
                        "/swagger-resources/**",
                        "/webjars/**", //以上是放行静态资源,以便knife4j正常运行
                        "/manager/login")
                .anonymous()
                //除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        //将session身份验证过滤器放到UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(sessionAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();

    }

集成了SpringSercurity后的登陆校验过滤器
//这个过滤器的作用就是在验证账号密码是否正确前,确定是否需要重新认证,如果为空就说明需要重新认证
//不为空就标记为已认证,不需要再认证了
@Component
public class SessionAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpSession session = request.getSession();
        LoginUser loginUser = (LoginUser)session.getAttribute(ManagerConstant.USER_LOGIN);

        //如果loginManager为空,有可能是用户登录态过期
        //那么这次请求有可能是登陆请求
        if (loginUser == null) {
            //放行,看看后面是否可以通过登陆时验证账号密码的过滤器
            filterChain.doFilter(request, response);
            return;
        }

        //调用三个参数的构造器是因为这个构造器会设置super.setAuthenticated(true);表示本次请求已进行身份验证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        //存入SecurityContextHolder容器中,后面过滤器会从这个容器中获取user对象
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行
        filterChain.doFilter(request,response);
    }
}
捕获并处理SpringSercurity中校验和授权的异常并处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    //捕获认证时异常
    @ExceptionHandler(AuthenticationException.class)
    public BaseResponse<?> authenticationExceptionHandler(AuthenticationException e) {
        log.error("AuthenticationServiceException", e);
        return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, e.getMessage());
    }

    //捕获授权时异常
    @ExceptionHandler(AccessDeniedException.class)
    public BaseResponse<?> accessDeniedExceptionHandler(AccessDeniedException e) {
        log.error("AccessDeniedException", e);
        return ResultUtils.error(ErrorCode.FORBIDDEN_ERROR, e.getMessage());
    }
}
登录态存储
  • 有状态应用:cookie+session+spring session redis

    • 如果有状态应用使用 JWT+Redis 来存储用户登陆态,JWT完全可以用一个UUID来代替
    • spring session redis是一个分布式Session,只需要引入依赖,然后正常使用ServletAPI即可,他会自动将Session信息存到Redis中
  • 无状态应用:JWT

Session过期时间
spring:
  servlet:
	 session:
    # 开启分布式 session(须先配置 Redis)
    	store-type: redis
    # 30 天过期 2592000  单位秒
	    timeout:  1
	    
	    
server:
  servlet:
    session:
      #非分布式session过期时间单位秒
      timeout: 1800
数据校验
  1. 引入依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  1. 加好注解
@Data
public class LoginManagerRequest implements Serializable {

    private static final long serialVersionUID = 2647726180879097414L;

    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空")
    private String username;

    /**
     * 用户密码
     */
    @NotBlank(message = "密码不能为空")
    private String userPassword;

}
  1. 在Controller中加好@Vaild注解
    @PostMapping("/login")
    public BaseResponse<LoginManagerVO> login(@RequestBody @Valid LoginManagerRequest) {
        //正常写代码
    }
  1. 配置好全局异常处理器,只返回message里的错误信息,不然会报错一个乱七八糟的对象给前端
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {    
	//捕获数据校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseResponse<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError != null) {
            String errorMessage = fieldError.getDefaultMessage();
            return ResultUtils.error(ErrorCode.PARAMS_ERROR, errorMessage);
        } else {
            // 如果没有找到字段错误,可以检查ObjectError中的错误
            ObjectError objectError = e.getBindingResult().getGlobalError();
            if (objectError != null) {
                String errorMessage = objectError.getDefaultMessage();
                return ResultUtils.error(ErrorCode.PARAMS_ERROR, errorMessage);
            }
        }
        // 如果以上都没有匹配到错误信息,则返回通用错误信息
        return ResultUtils.error(ErrorCode.PARAMS_ERROR, "请求参数错误");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值