SpringSecurity笔记2

配置类:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // 已登录的用户的token验证
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    // 登录失败
    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    // 认证失败
    @Autowired
    private AccessDeniedHandlerImpl accessDeniedHandler;

    // 前端用户登录的用户信息验证
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 密码加密解密
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);
        http
                // 关闭csrf
                .csrf().disable()
                // 不通过session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 放行
                .antMatchers("/login").anonymous()
                // 这个接口需要认证才能访问
                .antMatchers("/link/getAllLink").authenticated()
                .antMatchers("/logout").authenticated()
                // 其余的接口也放行
                .anyRequest().permitAll();
        // 关闭security的默认关闭接口
        http.logout().disable();
        // 配置过滤器
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 配置异常处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);
        // 允许跨域
        http.cors();
    }
}

用户登录流程:

@Service
public class BlogLoginServiceImpl implements BlogLoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public ResponseResult login(User user) {
        // 放入用户名和密码
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        // authenticate 会调用 UserDetailsServiceImpl 登录判断
        Authentication authenticate = authenticationManager.authenticate(authentication);
        // 判断是否认证通过
        if (authenticate == null) throw new RuntimeException("用户名或密码错误");
        // 获取userId 生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Long userId = loginUser.getUser().getId();
        String token = JwtUtil.createJWT(String.valueOf(userId));
        // 用户信息存入redis
        redisTemplate.opsForValue().set("bloglogin:" + userId, loginUser,1, TimeUnit.DAYS);
        // token和userinfo封装 返回
        BlogUserLoginVo vo = new BlogUserLoginVo();
        vo.setToken(token);
        vo.setUserInfo(BeanCopyUtils.copyBean(loginUser.getUser(), UserInfoVo.class));
        return ResponseResult.success(vo);
    }
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    // 登录判断
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username)) throw new RuntimeException("用户名不能为空");
        // 条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserName, username);
        // 查询
        User user = userMapper.selectOne(wrapper);
        if (user == null) throw new RuntimeException("用户不存在");
        return new LoginUser(user);
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

    private User user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 权限集合
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

用户登陆后,访问权限接口,token认证拦截器:

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    // 用户访问需要登录的接口,验证token
    @Autowired
    private ObjectMapper om;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            // 后面登录的security还会进行校验
            filterChain.doFilter(request, response);
            return;
        }
        // 解析token 获取userId
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            // 过期
            // 非法token
            // 响应前端重新登录
            ResponseResult result = new ResponseResult(401, "用户未登录", null);
            WebUtils.renderString(response, om.writeValueAsString(result));
            return;
        }
        String userId = claims.getSubject();
        // 从redis中获取用户信息
        LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get("bloglogin:" + userId);
        if (loginUser == null) {
            // redis里没有
            ResponseResult result = new ResponseResult(402, "登录已过期", null);
            WebUtils.renderString(response, om.writeValueAsString(result));
            return;
        }
        // 存入SecurityContextHolder
        // 未认证的是2个参数,已认证的是3个参数
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 放行
        filterChain.doFilter(request, response);
        return;
    }
}

以上完成后,拦截后不会返回前端json结果

自定义认证失败处理:

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {

    @Autowired
    private ObjectMapper om;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // 登录失败失败响应给前端
        authException.printStackTrace();
        // BadCredentialsException 用户名或密码错误
        // InsufficientAuthenticationException 需要登录
        ResponseResult result = null;
        if (authException instanceof BadCredentialsException)
            result = new ResponseResult(401, authException.getMessage(), null);
        else if (authException instanceof InsufficientAuthenticationException)
            result = new ResponseResult(402, "用户未登录", null);
        else
            result = new ResponseResult(403, authException.getMessage(), null);
        WebUtils.renderString(response, om.writeValueAsString(result));
    }
}

 下面这个是鉴权的响应,目前还用不到:

@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {

    @Autowired
    private ObjectMapper om;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        // 鉴权失败的响应
        accessDeniedException.printStackTrace();
        ResponseResult result = new ResponseResult(401, "无权限操作", null);
        WebUtils.renderString(response, om.writeValueAsString(result));
    }
}

统一异常处理:

@Data
@NoArgsConstructor
public class SystemException extends RuntimeException {
    private Integer code;
    private String msg;

    public SystemException(Integer code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
}

// 捕获Controller的异常
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    // 可控的异常
    @ExceptionHandler(SystemException.class)
    public ResponseResult SystemExceptionHandler(SystemException e) {
        // 打印日志
        log.error("出现了异常! {}", e);
        // 响应前端
        return new ResponseResult(e.getCode(), e.getMsg(), null);
    }

    // 不可控的异常
    @ExceptionHandler(Exception.class)
    public ResponseResult exceptionHandler(Exception e) {
        // 打印日志
        log.error("出现了异常! {}", e);
        // 响应前端
        return new ResponseResult(500, e.getMessage(), null);
    }
}

参考视频:

B站最通俗易懂手把手SpringBoot+Vue项目实战-前后端分离博客项目-Java项目_哔哩哔哩_bilibili

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Security是一个功能强大、灵活的身份验证和访问控制框架,用于保护基于Spring的应用程序。它提供了一套完整的认证和授权解决方案,可以帮助开发人员轻松地添加安全功能到他们的应用程序中。 以下是一些常见的Spring Security笔记: 1. Spring Security的核心概念:Spring Security基于一些核心概念,如认证(Authentication)、授权(Authorization)、用户(User)、角色(Role)等。了解这些概念对于理解和使用Spring Security非常重要。 2. 配置Spring Security:通过配置文件或Java代码,可以将Spring Security集成到应用程序中。配置文件中可以定义安全规则、用户信息、角色等。 3. 认证:认证是验证用户身份的过程。Spring Security支持多种认证方式,如基于表单、基于HTTP Basic认证、基于OAuth2等。开发人员可以选择适合自己应用程序的认证方式。 4. 授权:授权是决定用户是否有权限执行某个操作的过程。Spring Security提供了一套强大的授权机制,可以基于角色、权限、资源等进行访问控制。 5. 自定义认证和授权:通过实现Spring Security提供的接口或扩展现有的类,开发人员可以自定义认证和授权逻辑。这允许开发人员根据应用程序的特定需求定制安全策略。 6. 集成其他安全框架:Spring Security可以与其他安全框架(如OAuth2、LDAP、CAS等)进行集成,以满足不同应用程序的安全需求。 7. 日志和调试:Spring Security提供了详细的日志记录机制,可以帮助开发人员调试和排查安全问题。 以上是关于Spring Security的一些笔记,希望对你有帮助!如果你有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋劲豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值