基于Spring Security和JWT的登录鉴权系统

一、验证码生成

前台打开登录页面,调用接口/captchaImage

  • 定义一个字符串作为字符源,使用系统时间作为种子生成随机序列,通过字符源生成随机字符串
  • 生成UUID
  • 将验证码信息存入redis:key包含uuid,value为验证码,设置有效期(如2分钟,2分钟后redis删除该记录)
  • 生成验证码图片
  • 组装ResultVO(继承HashMap),返回验证码图片和uuid

二、登录方法

login方法传参:username,password,captchaCode,uuid
其中,uuid是后台生成验证码时返回给前台的标识

1. 验证码验证

通过uuid从redis中取出验证码

  • 没取到:抛出验证码失效异常
  • 不匹配:抛出验证码错误异常

2. Spring Security验证

调用Spring Security框架中的方法进行用户名和密码验证。

1)获取用户信息
  • 验证工作主要由AuthenticationManager这个接口的实例来完成,默认情况下,Spring Security会将其实例化为ProviderManager这个类。
  • 该类有一个authenticate方法,我们首先创建一个包含用户输入的用户名密码信息的Authentication对象,将该对象传入authenticate方法。
  • 之后,该方法会调用类中一个泛型为AuthenticationProvider接口的集合,真正的验证逻辑是由该集合中各个接口实现类来完成。
  • 这些实现类在验证用户时,都会调用UserDetailService实现类的loadUserByUsername方法来获取用户信息,这个UserDetailService实现类可以由我们自定义,可以根据实际需求定义成从数据库中获取用户信息等。
2)账密校验
  • AuthenticationProvider实现类调用PasswordEncoder的matches方法来实现校验。PasswordEncoder有多个实现类,分别对应多种密码编码器,默认是使用BCrypt散列哈希加密。

BCrypt加密解密

encode()编码

生成一个随机密码盐,这个密码盐每次都是不一样的。将随机密码盐和输入的密码进行组合,通过计算Hash和Base64编码等操作得到加密后的密码。因为每次用到的随机密码盐是不同的,所以每次使用BCrypt编码同一个密码,得到的结果都是不一样的。

matches()解密

根据数据库查询出来的编码后的密码拿到密码盐,利用这个密码盐进行Hash和Base64等操作,看是否和用户输入的密码相同。虽然每次BCrypt编码同一个密码结果都不同,但同一个密码盐+原密码的编码结果是相同的,所以只要在从数据库中查到的密码中获取密码盐,即可进行匹配。

  • 账密验证通过后,Spring Security会抹去密码信息,将获取到的用户详细信息传出。

3. 生成JWT

  • 生成UUID,将uuid作为key,当前用户详细信息作为value,存入redis,并设置过期时间
  • 定义一个Map类型的claims,将uuid作为value存入
  • 生成JWT,claims作为payload:
    header的base64编码 . claims的base64编码 . 签名(传入“header的编码 . claims的base64编码”,使用密钥和指定算法签名)

三、鉴权方法

  • 新建一个Spring Security配置类,继承自WebSecurityConfigureAdapter,通过重写configure方法实现Spring Security的配置
  • 该方法中,我们可以定义允许匿名访问的资源,比如登录接口、验证码接口、一些静态资源等;可以定义验证失败后的处理类;同时添加一个JWTFilter
  • 这个JWTFilter会从请求的头部中取出token,从token中解析出uuid,到redis中获取用户信息
  • 如果获取成功,刷新token的过期时间,同时在上下文中添加验证信息
  • 如果获取失败,会调用验证失败处理类

你可能感兴趣的:(JavaEE,java,jwt,spring,boot)