SpringBoot笔记----SpringBoot整合JWT

595 阅读3分钟

JWT简介【第一位大哥解释了token是什么怎么用 第二个大哥详细解释了三部分的组成】

(18条消息) 初见Token、JWT结构和Java的JJWT实现JWT(简洁明了的了解Token,以及Token认证和Cookie认证的区别)_天道酬勤-CSDN博客_jjwt和java-jwt

基于jwt的token验证 - 奕锋博客 - 博客园 (cnblogs.com)

实现步骤

  1. maven引入依赖
  2. 生成token的工具类
  3. controller层接收前端的请求
  4. service层查询用户信息并生成token返回给前端
  5. 前端将信息保存在 cookie中

环境准备

  1. JWT依赖【这里使用的依赖是jjwt 支持更高级的应用 此外还有别的token插件 Auth0将依赖代码也放到这里了】
<!--        jwt 依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<!--另一种token插件 性能更高-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>
  1. token工具类【生成token 校验token 根据token获取私有部分的用户信息】
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;

import java.util.Date;

public class JwtHelper {

    // token 失效时间  当前设置过期时间 24小时
    private static long tokenExpiration = 24*60*60*1000;

    //生成token 的私有盐 这个按道理来说只有自己知道 加密的时候加入盐 token就不会轻易的被破解
    private static String tokenSignKey = "123456";

    /**
     * 生成jwt令牌【token】 也可以根据别的用户信息生成token 这里选择了 userId userName
     * @param userId  用户id
     * @param userName  用户名
     * @return
     */
    public static String createToken(Long userId,String userName){
          String token =  Jwts.builder()

                  //公共部分
                  .setSubject("YYGH-USER")
                  //设置token 的过期时间
                  .setExpiration(new Date(System.currentTimeMillis()+tokenExpiration))

                  //私有部分 
                  .claim("userId",userId)
                  .claim("userName",userName)

                  //签名部分 设置加密算法 + 自己的盐
                  .signWith(SignatureAlgorithm.HS512,tokenSignKey)

                  //对token的压缩方法 载荷过长可以进行压缩
                  .compressWith(CompressionCodecs.GZIP)
                  .compact();

          return token;
    }

    /**
     * 根据token 得到用户ID
     * @param token
     * @return
     */
    public static Long getUserIdByToken(String token){

        //判断token是否 null
        if(StringUtils.isEmpty(token)) return null;

        /**
         * 根据自定义的盐值解析token 获取token里面私有部分的信息
         *
         * tokenSignKey 自己设置的盐
         * token  传过来的token
         */
        Jws<Claims> claimsJwts =  Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);

        //私有部分的数据体
        Claims jwtsBody = claimsJwts.getBody();
        Long userId = (Long) jwtsBody.get("userId");
        return userId;
    }

    /**
     * 根据token 获取用户名称
     * @param token
     * @return
     */
    public static String getUserNameByToken(String token){
        if(StringUtils.isEmpty(token)) return "";
        Jws<Claims> claimsJws
                = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("userName");
    }
    

3.controller层代码

@Api(value = "用户相关接口")
@RestController
@RequestMapping("/api/user")
public class UserInfoApiController {

    @Autowired
    private UserInfoService userInfoService;

    @ApiOperation(value = "用户登录")
    @PostMapping("login")
    public R login(@RequestBody LoginVo loginVo, HttpServletRequest request){

        //获取用户登录的ip地址【这里是我自己封装的获取用户ip的工具类 你可以使用HuTool工具包等自己实现】
        loginVo.setIp(IpUtils.getIpAddr(request));

        //执行登录请求  当前登录的业务自己实现 不一定要跟我一样
        Map<String, Object> info = userInfoService.login(loginVo);

        //判断是否登录成功
        String message = (String) info.get("msg");
        if(!StringUtils.isEmpty(message)){
            return R.error().data("message",message);
        }

        //返回登录信息和token
        return R.ok().data(info);
    }
}

4.service层代码【我这里面的逻辑判断 有些麻烦 大家根据自己情况 实现不同的校验】


@Autowired
private RedisTemplate<String,String> redisTemplate;

@Override
public Map<String, Object> login(LoginVo loginVo) {

    Map<String, Object> map = new HashMap<>();

    /**
     * 获取手机号和验证码
     * 1:校验是否为空
     * 2:校验验证码是否正确
     * 3:校验手机号是否存在 不存在自动注册
     * 4:如果已存在 校验手机号手机否被删除或禁用
     */
    String phone = loginVo.getPhone();
    String code = loginVo.getCode();
    if(StringUtils.isEmpty(phone)){
        map.put("msg","手机号码不能为空");
        return map;
    }
    if(StringUtils.isEmpty(code)){
        map.put("msg","验证码不能为空");
        return map;
    }

    //TODO 校验验证码 从缓存区获取验证码 跟前端传过来的验证码做对比
    //当前我这里是SpringcLoud项目  整合了redis 不需要的同志可以直接删除【按照自己的逻辑来】
    String phoneCode = redisTemplate.opsForValue().get(phone);
    if(!code.equals(phoneCode)){
        map.put("msg","手机验证码错误");
        return map;
    }

    //手机号不存在 自动注册
    QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
    wrapper.eq("phone",phone);
    UserInfo userInfo = baseMapper.selectOne(wrapper);
    if(userInfo == null){
        userInfo = new UserInfo();
        userInfo.setName("");
        userInfo.setPhone(phone);
        userInfo.setStatus(1);
        baseMapper.insert(userInfo);
    }

    //如果账号存在 但是已经被禁用
    if(userInfo.getStatus() == 0){
        map.put("msg","账号已被禁用,请联系客服");
        return map;
    }

    //上面所有的校验都通过以后 Controller层返回用户信息
    String name = userInfo.getName();
    if(!StringUtils.isEmpty(name)){
        name = userInfo.getNickName();
    }
    if(StringUtils.isEmpty(name)) {
        name = userInfo.getPhone();
    }
    map.put("name",name);

    //jwt 生成 token 字符串
    String token = JwtHelper.createToken(userInfo.getId(),name);
    map.put("token",token);

    return map;
}

如何校验token和别的token工具的使用 这位大哥已经详细给我们说了

(18条消息) Token插件:Auth0和jjwt对比_进击的小白-CSDN博客

  1. 前端保存cookie信息 我这里使用的是 js-cookie 【前端是vue框架】

前端登录界面和计时器的实现可以参考我知乎文章【写的特别简陋 看不懂的朋友可以自己百度】

前端登录时按钮倒计时效果的实现? - 知乎 (zhihu.com)