SpringSecurity认证

一、Web登录流程

在这里插入图片描述

有以下缺点可以改进:

  • 现在使用的是security自带的登陆页面,比较丑。 想换成自己项目的,优化的登录页
  • 用户使用的是security给的用户名和密码。 想真实地去数据库里获取真实的用户名和密码
  • security自带的cookie\session模式。 想自己生成jwt,无状态登陆
  • 前端页面怎么携带jwt。 想请求头里带上
  • 鉴权操作完全没有。 想鉴权做完善。

总而言之,自己的一些特定需求,都没有实现

二、SpringSecurity源码查看

springsecurity 就是通过一些过滤器、拦截器,实现登陆鉴权的流程的

2.1 springsecurity 登陆流程

springsecurity就是一个过滤器链,内置了关于springsecurity的16个过滤器

在这里插入图片描述

注意:上图中只写出了几个核心过滤器,其他的如下图

  • UsernamePasswordAuthenticationFilter:处理我们登陆页面输入的用户名和密码是否正确的过滤器
  • ExceptionTranslationFilter:处理前面的几个过滤器中,有了问题,抛出错误,不让用户登录
  • FilterSecurityInterceptor:经行一个权限校验的拦截器

我们可以从boot项目中找到所有有关security的过滤器链

在这里插入图片描述

2.2 认证流程 再细化

debug UsernamePasswordAuthenticationFilter 运行机制

在这里插入图片描述

三、自定义登录

在这里插入图片描述

思路:
(1)登录:
a、自定义登录接口->调用prodivermanager auth方法->登陆成功生成jwt->存入redis
b、自定义userdetailsmanager实现类-> 从数据库中获取系统用户
(2)访问资源:
自定义认证过滤器->获取token-> 从token中获取userid-> 从redis中通过userid获取用户信息->​ 存SecurityContextHolder

3.1 JWT简介

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。无状态。
好处:不需要服务器端 存session
特点:可以被看到,但是不能篡改,因为第三部分用了秘钥

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

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

在头部指明了签名算法是HS256算法。 我们进行BASE64编码https://c.runoob.com/front-end/693/,编码后的字符串如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷(playload)

载荷就是存放有效信息的地方

定义一个payload:

{"sub":"1234567890","name":"liming","admin":true,"age":18}

然后将其进行base64加密,得到Jwt的第二部分

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImxpbWluZyIsImFkbWluIjp0cnVlLCJhZ2UiOjE4fQ==

签证(signature)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)

  • payload (base64后的)

  • secret

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

hs256("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImxpbWluZyIsImFkbWluIjp0cnVlLCJhZ2UiOjE4fQ==",secret)

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

aHMyNTYoImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUp6ZFdJaU9pSXhNak0wTlRZM09Ea3dJaXdpYm1GdFpTSTZJbXhwYldsdVp5SXNJbUZrYldsdUlqcDBjblZsTENKaFoyVWlPakU0ZlE9PSIsc2VjcmV0KQ==
3.2 JJWT签发与验证token

JJWT是一个提供端到端的JWT创建和验证的Java库
官网文档:https://github.com/jwtk/jjwt

创建token

1、新建项目jwtTest中的pom.xml中添加依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

2、创建测试类,代码如下:

@Test
public void testJwt(){
     JwtBuilder jwtBuilder = Jwts.builder()
             .setId("666")//设置id
             .setSubject("testJwt")//主题
             .setIssuedAt(new Date())//签发日期
             .signWith(SignatureAlgorithm.HS256, "liming");
     String jwt = jwtBuilder.compact();
     System.out.println(jwt);
 }

运行打印结果:

//再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiJ0ZXN0Snd0IiwiaWF0IjoxNjg1MjUzNjMwfQ.65OPPl5Y6C51XaEMU2l38oGf0ylHvcHHzxEw2FPKhis

解析token

我们刚才已经创建了token ,在web应用中这个操作是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),那服务端接到这个token 应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果

 @Test
 public void testJwt(){
      JwtBuilder jwtBuilder = Jwts.builder()
              .setId("666")//设置id
              .setSubject("testJwt")//主题
              .setIssuedAt(new Date())//签发日期
              .signWith(SignatureAlgorithm.HS256, "liming");
      String jwt = jwtBuilder.compact();
      System.out.println(jwt);
      
      Claims claims = Jwts.parser().setSigningKey("liming").parseClaimsJws(jwt).getBody();
      System.out.println(claims);
  }

运行打印效果:

{jti=666, sub=testJwt, iat=1685253964}

注意:设置签名key必须和生成时一致

设置过期时间

有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间

 @Test
 public void testJwt(){
      //当前时间
      long l = System.currentTimeMillis();
      Date date = new Date(l + 10000);


      JwtBuilder jwtBuilder = Jwts.builder()
              .setId("666")//设置id
              .setSubject("testJwt")//主题
              .setIssuedAt(new Date())//签发日期
              .setExpiration(date)//用于设置过期时间 ,参数为Date类型数据
              .signWith(SignatureAlgorithm.HS256, "liming");
      String jwt = jwtBuilder.compact();
      System.out.println(jwt);

      try {
          Thread.sleep(15000);
      } catch (InterruptedException e) {
          throw new RuntimeException(e);
      }

      Claims claims = Jwts.parser().setSigningKey("liming").parseClaimsJws(jwt).getBody();
      System.out.println(claims);
  }

打印效果:

在这里插入图片描述

当前时间超过过期时间,则会报错

自定义claims

我们刚才的例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims

 @Test
 public void testJwt(){
     JwtBuilder jwtBuilder = Jwts.builder()
             .setId("666")//设置id
             .setSubject("testJwt")//主题
             .setIssuedAt(new Date())//签发日期
             .claim("userId","123")
             .signWith(SignatureAlgorithm.HS256, "liming");
     String jwt = jwtBuilder.compact();
     System.out.println(jwt);

     Claims claims = Jwts.parser().setSigningKey("liming").parseClaimsJws(jwt).getBody();
     System.out.println(claims);
 }

运行效果:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiJ0ZXN0Snd0IiwiaWF0IjoxNjg1MjU1ODk1LCJ1c2VySWQiOiIxMjMifQ.UdeEEeq3oRg8wwImn0DVFMm35uw__xuyXdgBAADLe8g
{jti=666, sub=testJwt, iat=1685255895, userId=123}
3.2 准备新项目(AuthDemo)

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小钟不想敲代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值