JWT
登录使用JWT技术。
JWT 可以生成 一个加密的 token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。
请求需要登录的资源或者接口的时候,将token携带,后端验证token是否合法。
JWT 有三部分组成:A.B.C
-
A:Header,{“type”:“JWT”,“alg”:“HS256”} 固定
-
B:playload,存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息
-
C: 签证,A和B加上秘钥 加密而成,只要秘钥不丢失,可以认为是安全的。
JWT验证,主要就是验证C部分 是否合法。
依赖包
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
创建工具类
package top.lingchen.blogapi.utils;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Author 凌宸
* @create 2021-12-16 下午 4:57
* @Description
* @Version 1.0
*/
public class JWTUtils {
private static final String jwtToken = "@lingchen1120.com!@#$$"; // 密钥
public static String createToken(Long userId){
Map<String,Object> claims = new HashMap<>();
claims.put("userId",userId);
JwtBuilder jwtBuilder = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, jwtToken) // 签发算法,秘钥为jwtToken
.setClaims(claims) // body数据,要唯一,自行设置
.setIssuedAt(new Date()) // 设置签发时间, // 一天的有效时间
.setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000));
String token = jwtBuilder.compact();
return token;
}
public static Map<String, Object> checkToken(String token){
try {
Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
return (Map<String, Object>) parse.getBody();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
// 在这可以测试查看生成的 token
}
}
配合之前的 Redis 缓存使用,将令牌 token 存入 Redis。
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import top.lingchen.blogapi.bean.SysUser;
import top.lingchen.blogapi.service.LoginService;
import top.lingchen.blogapi.service.SysUserService;
import static top.lingchen.blogapi.vo.ErrorCode.*;
import top.lingchen.blogapi.utils.JWTUtils;
import top.lingchen.blogapi.vo.Result;
import top.lingchen.blogapi.vo.params.LoginParam;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Author 凌宸
* @create 2021-12-16 下午 5:05
* @Description
* @Version 1.0
*/
@Service
@Transactional
public class LoginServiceImpl implements LoginService {
@Autowired
private SysUserService sysUserService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 加密盐
private static final String slat = "@1642.lingchen!@#";
@Override
public Result login(LoginParam loginParam) {
/**
* 1.检查参数是否合法
* 2. 根据用户名和密码去 user 表中查询记录是否存在
* 3.如果不存在,登录失败
* 4.如果存在,使用 JWT 生成 token 返回给前端
* 5. 将 token 放入 redis 中, redis token:user 信息,设置过期时间
* (登录验证时,先验证 token 字符串是否合法, 去 redis 认证是否存在)
*/
String account = loginParam.getAccount();
String password = loginParam.getPassword();
if(!StringUtils.hasLength(account) || !StringUtils.hasLength(password)){
return Result.fail(PARAMS_ERROR.getCode(), PARAMS_ERROR.getMsg());
}
password = DigestUtils.md5Hex(password + slat);
SysUser sysUser = sysUserService.findUser(account, password);
if(sysUser == null){
return Result.fail(ACCOUNT_PWD_NOT_EXIST.getCode(), ACCOUNT_PWD_NOT_EXIST.getMsg());
}
// 根据登录用户的 id 生成 token,并存入 redis 服务器
String token = JWTUtils.createToken(sysUser.getId());
stringRedisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(sysUser), 1, TimeUnit.DAYS);
return Result.success(token);
}
}