最近因为项目需求,需采用token的方式实现登录认证,而不再使用session的方式登录,因而采用springboot集成JWT生成token实现登录认证。
1.首先添加jwt所需jar包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
2.接下来根据需要建一个工具类JwtUtils。
/**
* jwt工具类
*/
@ConfigurationProperties(prefix = "dingapp.jwt")
@Component
public class JwtUtils {
private Logger logger = LoggerFactory.getLogger(getClass());
private String secret;
private long expire;
private String header;
/**
* 生成jwt token
*/
public String generateToken(String companyId,String userId,Long expireTime, YAMLUtils.SysType st) {
Date nowDate = new Date();
Long v = expire;
if(expireTime != null){
v = expireTime;
}
//过期时间
Date expireDate = new Date(nowDate.getTime() + v * 1000);
return Jwts.builder().claim("SysType",st.getType())
.setHeaderParam("typ", "JWT")
.setId(companyId)
.setSubject(userId)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 解析token
*/
public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
logger.debug("validate is token error ", e);
return null;
}
}
/**
* token是否过期
* @return true:过期
*/
public boolean isTokenExpired(Date expiration) {
return expiration.before(new Date());
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
这个工具类是通过jwt生成token以及对token的解析,这里对token设置了过期时间,如果token解析不出或者说过期则会抛出异常。
3.工具类写好后,可以在登录时验证完登录密码生成token并返回给前端。
@PostMapping("/login")
public ApiResult login(@RequestBody SysUser user, HttpServletRequest request) {
ApiResult result = new ApiResult();
//多类型账号登陆
SysUser userBySomeCase = userService.getUserByAcc(user);
Integer landType = user.getType();
SysUser u;
u = new SysUser();
u.setAccNum(user.getAccNum());
u = userService.getUserByAcc(u);
if(u != null && !Objects.equals(u.getPasswd(),CommonUtil.encryptPsw(user.getPasswd()))){
return result.failed("账号或密码错误");
}
}
if(u == null){
return result.failed("账号或密码错误");
}
SysUser data = u;
//
String token = jwtUtils.generateToken(null,u.getUserID()+"",null,YAMLUtils.SysType.SysType3);
data.setToken(token1);
return result.success(data, "登录成功");
}
4.当用户请求接口时会在header中携带token,后端会对所有接口在前置拦截器进行拦截,作用是对拿到的token进行解析,如果解析失败,会抛出SignatureException异常,如果解析成功,则不会拦截。
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");
if(StringUtils.isBlank(token )){
throw new IncorrectCredentialsException("请先登录");
}
final Claims claims = jwtUtils.getClaimByToken(token);
if (claims == null || jwtUtils.isTokenExpired(claims.getExpiration())) {
throw new IncorrectCredentialsException("token失效,请重新登录");
}
return true;
}
5.可以对jwt进行全局配置。
dingapp:
jwt:
# 加密秘钥
secret: dfjlaer********
# token有效时长,24小时,单位秒
expire: 86400
header: token`在这里插入代码片`
总结:
1.因为jwt生成token的无状态性,以及便于传输,构成非常简单,字节占用很小,它不需要在服务端保存
会话信息, 所以它易于应用的扩展。
2.但一旦拿到访问的token,就可直接访问到服务器,所以对于私钥的保护非常重要。