我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringBoot SpringSecurity JWT实现系统安全策略详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringBoot SpringSecurity JWT实现系统安全策略详解

security进行用户验证和授权;jwt负责颁发令牌与校验,判断用户登录状态

一、原理

1. SpringSecurity过滤器链

SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链。

  • SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中。
  • LogoutFilter:用于处理退出登录。
  • UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。
  • BasicAuthenticationFilter:检测和处理 http basic 认证。
  • ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
  • FilterSecurityInterceptor:可以看做过滤器链的出口。

流程说明:客户端发起一个请求,进入 Security 过滤器链。

当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。

当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。

当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。

2. JWT校验

首先前端一样是把登录信息发送给后端,后端查询数据库校验用户的账号和密码是否正确,正确的话则使用jwt生成token,并且返回给前端。以后前端每次请求时,都需要携带token,后端获取token后,使用jwt进行验证用户的token是否无效或过期,验证成功后才去做相应的逻辑。

二、Security+JWT配置说明

1. 添加maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. securityConfig配置


@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    LoginFailureHandler loginFailureHandler;
    @Autowired
    LoginSuccessHandler loginSuccessHandler;
    @Autowired
    JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    JwtAccessDeniedHandler jwtAccessDeniedHandler;
    @Autowired
    UserDetailServiceImpl userDetailService;
    @Autowired
    JWTLogoutSuccessHandler jwtLogoutSuccessHandler;
    @Autowired
    CaptchaFilter captchaFilter;
    @Value("${security.enable}")
    private Boolean securityIs = Boolean.TRUE;
    @Value("${security.permit}")
    private String permit;
    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        //此处可添加别的规则,目前只设置 允许双 //
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }
    @Bean
    JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager(), jwtAuthenticationEntryPoint);
        return jwtAuthenticationFilter;
    }
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.cors().and().csrf().disable()
                // 登录配置
                .formLogin()
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)
                .and()
                .logout()
                .logoutSuccessHandler(jwtLogoutSuccessHandler)
                // 禁用session
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 配置拦截规则
                .and()
                .authorizeRequests()
                .antMatchers(permit.split(",")).permitAll();
        if (!securityIs) {
            http.authorizeRequests().antMatchers("/**").permitAll();
        }
        registry.anyRequest().authenticated()
                // 异常处理器
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler)
                // 配置自定义的过滤器
                .and()
                .addFilter(jwtAuthenticationFilter())
                // 验证码过滤器放在UsernamePassword过滤器之前
                .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class);
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }
}

3. JwtAuthenticationFilter校验token

package cn.piesat.gf.filter;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.piesat.gf.dao.user.SysUserDao;
import cn.piesat.gf.model.entity.user.SysUser;
import cn.piesat.gf.exception.ExpiredAuthenticationException;
import cn.piesat.gf.exception.MyAuthenticationException;
import cn.piesat.gf.service.user.impl.UserDetailServiceImpl;
import cn.piesat.gf.utils.Constants;
import cn.piesat.gf.utils.JwtUtils;
import cn.piesat.gf.utils.Result;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private AuthenticationEntryPoint authenticationEntryPoint;
    private AuthenticationManager authenticationManager;
    @Autowired
    JwtUtils jwtUtils;
    @Autowired
    UserDetailServiceImpl userDetailService;
    @Autowired
    SysUserDao sysUserRepository;
    @Autowired
    RedisTemplate redisTemplate;
    @Value("${security.single}")
    private Boolean singleLogin = false;
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
        Assert.notNull(authenticationManager, "authenticationManager cannot be null");
        Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
        this.authenticationManager = authenticationManager;
        this.authenticationEntryPoint = authenticationEntryPoint;
    }
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String jwt = request.getHeader(jwtUtils.getHeader());
        // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
        // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
        if (StrUtil.isBlankOrUndefined(jwt)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            Claims claim = jwtUtils.getClaimsByToken(jwt);
            if (claim == null) {
                throw new MyAuthenticationException("token 异常");
            }
            if (jwtUtils.isTokenExpired(claim)) {
                throw new MyAuthenticationException("token 已过期");
            }
            String username = claim.getSubject();
            Object o1 = redisTemplate.opsForValue().get(Constants.TOKEN_KEY + username);
            String o = null;
            if(!ObjectUtils.isEmpty(o1)){
                o = o1.toString();
            }
            if (!StringUtils.hasText(o)) {
                throw new ExpiredAuthenticationException("您的登录信息已过期,请重新登录!");
            }
            if (singleLogin && StringUtils.hasText(o) && !jwt.equals(o)) {
                throw new MyAuthenticationException("您的账号已别处登录,您已下线,如有异常请及时修改密码!");
            }
            // 获取用户的权限等信息
            SysUser sysUser = sysUserRepository.findByUserName(username);
            // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userDetailService.getUserAuthority(sysUser.getUserId()));
            SecurityContextHolder.getContext().setAuthentication(token);
            chain.doFilter(request, response);
        } catch (AuthenticationException e) {
            log.error(ExceptionUtil.stacktraceToString(e));
            authenticationEntryPoint.commence(request, response, e);
            return;
        } catch (Exception e){
            log.error(ExceptionUtil.stacktraceToString(e));
            response.getOutputStream().write(JSONUtil.toJsonStr(Result.fail(e.getMessage())).getBytes(StandardCharsets.UTF_8));
            response.getOutputStream().flush();
            response.getOutputStream().close();
            return;
        }
    }
}

4. JWT生成与解析工具类

package cn.piesat.gf.utils;
import cn.hutool.core.exceptions.ExceptionUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
@Data
@Component
@ConfigurationProperties(prefix = "jwt.config")
@Slf4j
public class JwtUtils {
    private long expire;
    private String secret;
    private String header;
    // 生成JWT
    public String generateToken(String username) {
        Date nowDate = new Date();
        Date expireDate = new Date(nowDate.getTime() + 1000 * expire);
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(username)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    // 解析JWT
    public Claims getClaimsByToken(String jwt) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (Exception e) {
            log.error(ExceptionUtil.stacktraceToString(e));
            return null;
        }
    }
    // 判断JWT是否过期
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }
}

到此这篇关于SpringBoot SpringSecurity JWT实现系统安全策略详解的文章就介绍到这了,更多相关SpringBoot SpringSecurity JWT内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringBoot SpringSecurity JWT实现系统安全策略详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SpringBoot SpringSecurity JWT实现系统安全策略详解

Spring Security是Spring的一个核心项目,它是一个功能强大且高度可定制的认证和访问控制框架。它提供了认证和授权功能以及抵御常见的攻击,它已经成为保护基于spring的应用程序的事实标准
2022-11-21

SpringBoot+SpringSecurity+JWT实现系统认证与授权示例

本文主要介绍了SpringBoot+SpringSecurity+JWT实现系统认证与授权示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2022-11-13

win8系统本地安全策略常见问题详细解答

下面是关于win8本地安全策略的常见问题解答,大家可以参考一下。 怎么打开“Windows本地安全策略”啊?答:“搜索”键入“secpol.msc”后回车。 如何防止黑
2022-06-04

win10系统没有本地安全策略如何解决

在某些版本的Windows 10中,本地安全策略可能没有包含在默认安装中。您可以按照以下步骤来解决这个问题:1. 打开“控制面板”。2. 在“控制面板”窗口中,选择“程序”。3. 在“程序”窗口中,选择“打开或关闭Windows功能”。4.
2023-09-12

Oracle API集成策略解析:实现系统间无缝通信

Oracle API集成策略解析:实现系统间无缝通信,需要具体代码示例在当今数字化时代,企业内部系统之间需要相互通信和数据共享,而Oracle API就是帮助实现系统间无缝通信的重要工具之一。本文将从Oracle API的基本概念和原理入
Oracle API集成策略解析:实现系统间无缝通信
2024-03-07

系统设置用户密码的功能大全(利用组策略实现)

在使用电脑的时候我们经常遇到各种各样的用户问题,如怎么设置用户密码的有效期,密码到期前提示用QaSugb户更改密码?如怎么设置用户密码的复杂性,这样可以设置更难破解的密码等问题。1 点击开始 - 运行 - 输入“gpedit.m
2023-05-29

SpringBoot实现点餐系统的登录与退出功能流程详解

结束了Springboot+MyBatisPlus也是开始了项目之旅,将从后端的角度出发来整理这个项目中重点业务功能的梳理与实现
2022-11-13

操作系统安全审计:为何重要?如何实施?一文详解,护卫网络安全

操作系统安全审计是维护网络安全的重要手段,通过对操作系统进行定期安全检查,及时发现安全漏洞并进行修复,可以有效抵御网络攻击,护卫网络安全。
操作系统安全审计:为何重要?如何实施?一文详解,护卫网络安全
2024-02-28

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录