JWT+SpringSecurity登录和权限管理

一、什么是JWT

说起JWT,我们应该来谈一谈基于token的认证和传统的session认证的区别。说起JWT,我们应该来谈一谈基于token的认证和传统的session认证的区别。

(1)、session所存在的问题

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

(2)、Token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,也就是说token认证机制的应用不需要去考虑用户在哪一台服务器登录了。

(3)、认识Token

JWT是由三段信息构成的,以 点(.) 分割,每部分都有不同的含义(每段都是用 Base64 编码的)
第一部分为 头部(Header)
第二部分为 载荷(Payload)
第三部分为 签证(Signature)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwiZXhwIjoxNjI1NDY3MDY5LCJ1c2VyTmFtZSI6IumBlW_mCIsImlhdCI6MTYyNTQ2NTI2OX0.e_uuksv0b8gqX9HUVEiieLQlKFKcLdxCxovJ3xA3wB8

第一部分通过Base64解码出的结果是

{
"typ":"JWT",
"alg":"HS256"
}

由此可以得知jwt的头部承载两部分信息 类型和加密算法

第二部分是用来放主要的存储信息的(主要信息中除了自定义信息还有 标准中注册的声明

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

当然以上是统一标准而已,并非必须用,建议不强制。

第三部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐 secret组合加密,然后就构成了jwt的第三部分。

二、使用JWT

(1)、导入依赖

<dependency>
    <groupid>com.auth0</groupid>
    <artifactid>java-jwt</artifactid>
    <version>3.10.3</version>
</dependency>

(2)、创建JwtUtils工具类

    @Value("{Jwt.secret}")
    private static String secret;

    /**
     &#x7B7E;&#x53D1;&#x5BF9;&#x8C61;&#xFF1A;&#x968F;&#x610F;
     &#x7B7E;&#x53D1;&#x65F6;&#x95F4;&#xFF1A;&#x73B0;&#x5728;
     &#x6709;&#x6548;&#x65F6;&#x95F4;&#xFF1A;30&#x5206;&#x949F;
     &#x8F7D;&#x8377;&#x5185;&#x5BB9;&#xFF1A;&#x81EA;&#x5B9A;&#x4E49;&#x5185;&#x5BB9;
     &#x52A0;&#x5BC6;&#x5BC6;&#x94A5;&#xFF1A;&#x76D0; + &#x5BC6;&#x94A5;
     */
    public static String createToken(String userId,String userName) {

        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.MINUTE,30);
        Date expiresDate = nowTime.getTime();
        //&#x7B7E;&#x53D1;&#x5BF9;&#x8C61;
        return JWT.create().withAudience(userId)
                //&#x53D1;&#x884C;&#x65F6;&#x95F4;
                .withIssuedAt(new Date())
                //&#x6709;&#x6548;&#x65F6;&#x95F4;
                .withExpiresAt(expiresDate)
                //&#x8F7D;&#x8377;&#xFF0C;&#x968F;&#x4FBF;&#x5199;&#x51E0;&#x4E2A;&#x90FD;&#x53EF;&#x4EE5;&#xFF0C;&#x4E5F;&#x53EF;&#x4EE5;&#x7406;&#x89E3;&#x4E3A;&#x81EA;&#x5B9A;&#x4E49;&#x53C2;&#x6570;
                .withClaim("userName", userName)
                //&#x52A0;&#x5BC6;
                .sign(Algorithm.HMAC256(secret+"&#x4F60;&#x968F;&#x610F;&#x5199;"));
    }

    /**
     * &#x68C0;&#x9A8C;&#x5408;&#x6CD5;&#x6027;&#xFF0C;&#x5176;&#x4E2D;secret&#x53C2;&#x6570;&#x5C31;&#x5E94;&#x8BE5;&#x4F20;&#x5165;&#x7684;&#x662F;&#x7528;&#x6237;&#x7684;id
     * @param token
     */
    public static void verifyToken(String token){
        DecodedJWT jwt = null;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+"WDNMD")).build();
            jwt = verifier.verify(token);
        } catch (Exception e) {
            //&#x6548;&#x9A8C;&#x5931;&#x8D25;
            //&#x8FD9;&#x91CC;&#x629B;&#x51FA;&#x7684;&#x5F02;&#x5E38;&#x662F;&#x6211;&#x81EA;&#x5B9A;&#x4E49;&#x7684;&#x4E00;&#x4E2A;&#x5F02;&#x5E38;&#xFF0C;&#x4F60;&#x4E5F;&#x53EF;&#x4EE5;&#x5199;&#x6210;&#x522B;&#x7684;

        }
    }

    /**
     * &#x83B7;&#x53D6;&#x7B7E;&#x53D1;&#x5BF9;&#x8C61;
     */
    public static String getAudience(String token) {
        String audience = null;
        try {
            audience = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j) {
            //&#x8FD9;&#x91CC;&#x662F;token&#x89E3;&#x6790;&#x5931;&#x8D25;

        }
        return audience;
    }

    /**
     * &#x901A;&#x8FC7;&#x8F7D;&#x8377;&#x540D;&#x5B57;&#x83B7;&#x53D6;&#x8F7D;&#x8377;&#x7684;&#x503C;
     */
    public static Claim getClaimByName(String token, String name){
        return JWT.decode(token).getClaim(name);
    }

三、JWT结合SpringSecurity实现登录鉴权以及权限管理

(1)、思路

登陆成功返回Token,并把Token存储到Redis中确保单点登录。使用过滤器校验Token和权限

(2)、SpringSecurity配置

由于使用Token进行登录鉴权,就不需要Session了,因此需禁用Session

@Component
@EnableWebSecurity
/**
 * &#x5F00;&#x542F;@EnableGlobalMethodSecurity(prePostEnabled = true)&#x6CE8;&#x89E3;
 * &#x5728;&#x7EE7;&#x627F; WebSecurityConfigurerAdapter &#x8FD9;&#x4E2A;&#x7C7B;&#x7684;&#x7C7B;&#x4E0A;&#x9762;&#x8D34;&#x4E0A;&#x8FD9;&#x4E2A;&#x6CE8;&#x89E3;
 * &#x5E76;&#x4E14;prePostEnabled&#x8BBE;&#x7F6E;&#x4E3A;true,@PreAuthorize&#x8FD9;&#x4E2A;&#x6CE8;&#x89E3;&#x624D;&#x80FD;&#x751F;&#x6548;
 * SpringSecurity&#x9ED8;&#x8BA4;&#x662F;&#x5173;&#x95ED;&#x6CE8;&#x89E3;&#x529F;&#x80FD;&#x7684;.

 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //&#x6CE8;&#x5165;&#x8FC7;&#x6EE4;&#x5668;
    @Resource
    private JwtVerificationFilter jwtVerificationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //&#x5173;&#x95ED;csrf&#x9632;&#x62A4; >&#x53EA;&#x6709;&#x5173;&#x95ED;&#x4E86;,&#x624D;&#x80FD;&#x63A5;&#x53D7;&#x6765;&#x81EA;&#x8868;&#x5355;&#x7684;&#x8BF7;&#x6C42;
        http.csrf().disable()
                .cors()//&#x5F00;&#x542F;&#x8DE8;&#x57DF;
                .and()
                //&#x5F00;&#x542F;&#x6388;&#x6743;&#x8BF7;&#x6C42;
                .authorizeRequests()
                //&#x653E;&#x884C;&#x63A5;&#x53E3;&#xFF0C;&#x56E0;&#x4E3A;&#x4F7F;&#x7528;&#x81EA;&#x5B9A;&#x4E49;&#x767B;&#x5F55;&#x9875;&#x9762;&#x6240;&#x4EE5;&#x9700;&#x8981;&#x653E;&#x884C;
                .antMatchers("/login/**").permitAll()
                //&#x62E6;&#x622A;&#x6240;&#x6709;&#x8BF7;&#x6C42;&#xFF0C;&#x6240;&#x6709;&#x8BF7;&#x6C42;&#x90FD;&#x9700;&#x8981;&#x767B;&#x5F55;&#x8BA4;&#x8BC1;
                .anyRequest().authenticated()
                .and()
                .addFilterAfter(jwtVerificationFilter, UsernamePasswordAuthenticationFilter.class)
                //&#x524D;&#x540E;&#x7AEF;&#x5206;&#x79BB;&#x91C7;&#x7528;JWT &#x4E0D;&#x9700;&#x8981;session&#xFF08;&#x6DFB;&#x52A0;&#x540E;Spring&#x6C38;&#x8FDC;&#x4E0D;&#x4F1A;&#x521B;&#x5EFA;session&#xFF09;
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

(3)、编写过滤器

/**
 * @author admin
 * &#x8FC7;&#x6EE4;&#x5668; &#x53D1;&#x8D77;&#x8BF7;&#x6C42;&#x524D;&#x68C0;&#x9A8C;Token &#x5B9E;&#x73B0;&#x5E76;&#x5728;&#x6BCF;&#x6B21;&#x8BF7;&#x6C42;&#x65F6;&#x53EA;&#x6267;&#x884C;&#x4E00;&#x6B21;&#x8FC7;&#x6EE4;
 * &#x5728;spring&#x4E2D;&#xFF0C;filter&#x90FD;&#x9ED8;&#x8BA4;&#x7EE7;&#x627F;OncePerRequestFilter
 * OncePerRequestFilter&#x987E;&#x540D;&#x601D;&#x4E49;&#xFF0C;&#x4ED6;&#x80FD;&#x591F;&#x786E;&#x4FDD;&#x5728;&#x4E00;&#x6B21;&#x8BF7;&#x6C42;&#x53EA;&#x901A;&#x8FC7;&#x4E00;&#x6B21;filter&#xFF0C;&#x800C;&#x4E0D;&#x9700;&#x8981;&#x91CD;&#x590D;&#x6267;&#x884C;
 * &#x4E3A;&#x4E86;&#x517C;&#x5BB9;&#x4E0D;&#x540C;&#x7684;web container&#xFF0C;&#x7279;&#x610F;&#x800C;&#x4E3A;&#x4E4B;
 *
 * &#x5728;servlet2.3&#x4E2D;&#xFF0C;Filter&#x4F1A;&#x7ECF;&#x8FC7;&#x4E00;&#x5207;&#x8BF7;&#x6C42;&#xFF0C;&#x5305;&#x62EC;&#x670D;&#x52A1;&#x5668;&#x5185;&#x90E8;&#x4F7F;&#x7528;&#x7684;forward&#x8F6C;&#x53D1;&#x8BF7;&#x6C42;&#x548C;<%@ include file="&#x201D;/login.jsp&#x201D;%">&#x7684;&#x60C5;&#x51B5;
 *
 * servlet2.4&#x4E2D;&#x7684;Filter&#x9ED8;&#x8BA4;&#x60C5;&#x51B5;&#x4E0B;&#x53EA;&#x8FC7;&#x6EE4;&#x5916;&#x90E8;&#x63D0;&#x4EA4;&#x7684;&#x8BF7;&#x6C42;&#xFF0C;forward&#x548C;include&#x8FD9;&#x4E9B;&#x5185;&#x90E8;&#x8F6C;&#x53D1;&#x90FD;&#x4E0D;&#x4F1A;&#x88AB;&#x8FC7;&#x6EE4;&#xFF0C;
 */
@Component
@Slf4j
public class JwtVerificationFilter extends OncePerRequestFilter {
    @Resource
    private RoleService roleService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private RolePermissionService rolePermissionService;

    /**
     * &#x8FC7;&#x6EE4;&#x5668;&#xFF0C;&#x68C0;&#x9A8C;Token
     * &#x53D1;&#x8D77;&#x8BF7;&#x6C42;&#x65F6;&#x4F1A;&#x8C03;&#x7528;&#x4E24;&#x6B21;&#xFF0C;&#x7B2C;&#x4E8C;&#x6B21;&#x662F;&#x5C55;&#x793A;/favicon.ico
     *
     */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, @NotNull FilterChain filterChain) throws ServletException, IOException {
        //&#x83B7;&#x53D6;Token
        String token = httpServletRequest.getHeader("token");

        //&#x975E;&#x7A7A;&#x6821;&#x9A8C;
        if (token == null) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        //&#x68C0;&#x9A8C;Token&#x5408;&#x6CD5;&#x6027;
        JwtUtils.getAudience(token);
        //&#x6BD4;&#x5BF9;Redis&#x4E2D;&#x5B58;&#x50A8;&#x7684;Token
        String redisToken = RedisUtils.get(RedisPrefixKey.LOGIN_TOKEN.keyAppend(JwtUtils.getAudience(token)).getKey())
                .toString();
        if (!redisToken.equals(token)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        //&#x83B7;&#x53D6;&#x6743;&#x9650;                                                             &#x6839;&#x636E;Token&#x83B7;&#x53D6;&#x8F7D;&#x8377;&#x7684;&#x503C;
        List<grantedauthority> authorityList = this.findAllAuthority(Long.valueOf(JwtUtils.getAudience(token)));

        //&#x5B89;&#x5168;&#x4E0A;&#x4E0B;&#x6587;&#xFF0C;&#x5B58;&#x50A8;&#x8BA4;&#x8BC1;&#x6388;&#x6743;&#x7684;&#x76F8;&#x5173;&#x4FE1;&#x606F;&#xFF0C;&#x5B9E;&#x9645;&#x4E0A;&#x5C31;&#x662F;&#x5B58;&#x50A8;"&#x5F53;&#x524D;&#x7528;&#x6237;"&#x8D26;&#x53F7;&#x4FE1;&#x606F;&#x548C;&#x76F8;&#x5173;&#x6743;&#x9650;
        SecurityContextHolder
                .getContext()
                .setAuthentication(new UsernamePasswordAuthenticationToken(null,null,authorityList));

        //&#x5C06;&#x8BF7;&#x6C42;&#x8F6C;&#x53D1;&#x7ED9;&#x8FC7;&#x6EE4;&#x5668;&#x94FE;&#x4E0B;&#x4E00;&#x4E2A;filter
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    /**
     * &#x67E5;&#x627E;&#x6743;&#x9650;
     */
    private List<grantedauthority> findAllAuthority(Long userId){
        //1&#x3001;&#x62FF;&#x5230;&#x7528;&#x6237;&#x7684;&#x89D2;&#x8272;&#x548C;&#x6743;&#x9650;
        //2&#x3001;&#x8FD4;&#x56DE;&#x7684;&#x6743;&#x9650;
        List<grantedauthority> authorityList = new ArrayList<>();
        //3&#x3001;&#x67E5;&#x51FA;&#x6743;&#x9650;&#x5217;&#x8868;&#x5FAA;&#x73AF;&#x653E;&#x5165;  authorityList  &#x4E2D;
        for (&#x6743;&#x9650;&#x5B9E;&#x4F53;&#x7C7B; url : &#x6743;&#x9650;&#x96C6;&#x5408;) {
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(&#x6743;&#x9650;&#x7684;url);
            authorityList.add(simpleGrantedAuthority);
        }
        return authorityList;
    }
}
</grantedauthority></grantedauthority></grantedauthority></%@>

迷途者寻影而行

Original: https://www.cnblogs.com/pkkyh/p/14972556.html
Author: 迷途者寻影而行
Title: JWT+SpringSecurity登录和权限管理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/575741/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • SQL Server如何修改登录密码

    SQL Server如何修改登录密码 我们在打开SQLserver的时候一般选择的是”Windows身份认证”进行登录,如果选择混合模式该怎么登录呢?或者忘…

    数据库 2023年6月9日
    092
  • MySQL日志

    一、错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时…

    数据库 2023年5月24日
    0104
  • 记一次stormOOM异常的产生与解决

    最近这段时间开始了一个新项目,项目使用rabbitMQ存储采集数据,通过storm对rabbitMQ中的数据进行实时计算,将结果存入到rabbitMQ的另一个队列中,再由另外一个s…

    数据库 2023年6月6日
    078
  • 数据库

    建库操作 #创建数据库(默认字符集编码) create database test20210420 #创建数据库的时候指定字符集编码以及字符校验规则 create database…

    数据库 2023年6月16日
    0120
  • Java8-Stream流

    Java8-Stream基础操作 JAVA技术交流群:737698533 在学习Stream之前必须有Lambda,的基础 Stream是Java8的新特性,可以进行对集合进行一些…

    数据库 2023年6月16日
    092
  • HA: FORENSICS靶机练习

    ubuntu拿到手,没有恢复模式,不好绕密码,仿真软件又会更改所有用户的密码,怕影响后续操作,先不采用,先试试用john跑一下看看能不能跑出一两个来。 刚好跑出来一个,用户 &lt…

    数据库 2023年6月11日
    089
  • 有趣的BUG之Stack Overflow

    今天遇到一个很有意思的bug,当程序开发完成后打包到服务器运行,总是会出现栈溢出异常,经过排查发现,问题出现在一个接口上,但这个接口逻辑并不复杂,除了几局逻辑代码外和打印语句之外也…

    数据库 2023年6月6日
    089
  • Java中的命名规则

    在查找java命名规则时,未在国内相关网站查找到较为完整的文章,这是一篇国外程序开发人员写的java命名规则的文章,原文是英文写的,为了便于阅读,遂翻译为汉语,以便帮助国内开发者有…

    数据库 2023年6月11日
    092
  • MySQL实战45讲 19

    19 | 为什么我只查一行的语句,也执行这么慢? 有些情况下,”查一行”,也会执行得特别慢。 需要说明的是,如果 MySQL 数据库本身就有很大的压力,导致…

    数据库 2023年6月14日
    071
  • Java 考试系统项目源码 springboot mybaits vue.js 支持手机端考试

    新增功能:培训学习模块, PDF电子课程、视频课程、直播课程(自己搭建直播流服务器) 人脸识别(考试时验证,有开关)、补考开关 组建试卷:创建试卷,题目、类型、总分、及格分数、时长…

    数据库 2023年6月6日
    082
  • MySQL(一)——查看密码与修改

    查看数据库密码,策略与修改 RPM安装: 源码安装: 进入:数据库 进入数据库后第一步设置密码: 查看密码策略 修改密码策略,长度 0宽容模式 混合模式,0关闭大小写 特殊字符 O…

    数据库 2023年6月14日
    0121
  • 【干货总结】:可能是史上最全的MySQL和PGSQL对比材料

    版权情况:PostgreSQL 11(免费开源)、MySQL5.7 Oracle官方社区版(免费开源)1. CPU限制 PGSQL 没有CPU核心数限制,有多少CPU核就用多少 M…

    数据库 2023年6月9日
    068
  • 【JDBC】笔记(1)— JDBC概述

    1、JDBC是什么?Java DataBase Connectivity(Java语言连接数据库) 2、JDBC的本质是什么?JDBC是SUN公司制定的一套 接口(实质)java….

    数据库 2023年5月24日
    096
  • 应用层

    应用层 应用层概述 应用层是计算机网络体系结构的最顶层,是设计和建立计算机网络的最终目的,也是计算机网络中发展最快的部分。 早期基于文本的应用(电子邮件、远程登录、文件传输、新闻组…

    数据库 2023年6月9日
    086
  • MySQL8.0其他新特性

    MySQL8.0其他新特性 MySQL8.0新特性概述 MySQL8.0新增特性 MySQL8.0移除的旧特性 新特性1:窗口函数 窗口函数的分类 MySQL8.0版本开始支持窗口…

    数据库 2023年5月24日
    078
  • 云数据库技术|“重磅升级”后再测 TDSQL-C

    标题 1.摘要 前段时间,测试了国内主要云原生数据库 PolarDB、TDSQL-C、GaussDB 的性能,参考:《再测云原生数据库性能》。在上次测试结果中,由于地域版本差异,腾…

    数据库 2023年6月11日
    085
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球