目录
一、Session和JWT的对比
Session流程:
每个用户第一次访问服务器后,服务器都要创建Session并保存在服务器中,而下一次访问,服务器就会根据保存的session来对请求进行校验。
缺点:
1.对服务器压力大: 用户增加的时候,服务器开销增大。
2.程序扩展性差:用户认证之后,服务器作记录,在分布式的应用上,限制了负载均衡器的能力,也就限制了扩展的能力。
3.安全性偏低:因为基于cookie进行用户识别的,cookie被截获,用户就容易收到跨站请求伪造的攻击。
JWT流程:
组成:
头部Header:存储JWT配置信息
载荷PayLoad:存储需要保存的信息,例如user_id,auth
密钥Signature:使用对应算法对头部和载荷进行签名,防止Token被篡改
流程:
1.在用户登录成功后将签发的token返回,将验证信息存储在客户端
2.用户发起请求时携带token后端对token进行验证并执行对应的方法
3用户退出登录或者修改重要信息后在客户端销毁token
优缺点对比:
session默认储存在内存中(可以修改为保存 为文件或者Redis中),如果把代码部署在多台服务器上需要使用Redis进行内网访问,JWT只要有秘钥就可以实现单点登录
JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权 限,一旦JWT签发,在有效期内将会一直有效,所以开发时经常设置专门的黑名单,可以说是Session保存用户白名单,JWT保存的是用户黑名单
二、JWT的使用
1.添加依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.0</version>
</dependency>
2.Controller控制器类
ApiRestResponse为封装类,封装后端信息返回给前端,
Constant类保存常量信息。
@GetMapping("/loginWithJwt")
public ApiRestResponse loginWitJwt(@RequestParam("userName") String userName,
@RequestParam("password") String password) {
if (!StringUtils.hasText(userName)) {
return ApiRestResponse.error(ExceptionEnum.NEED_USER_NAME);
}
if (!StringUtils.hasText(password)) {
return ApiRestResponse.error(ExceptionEnum.NEED_PASSWORD);
}
User user = userService.login(userName, password);
// 保存用户信息时不保存密码
user.setPassword(null);
Algorithm algorithm = Algorithm.HMAC256(Constant.JWT_KEY);
// 生成令牌
String token = JWT.create().withClaim(Constant.USER_NAME, user.getUsername())
.withClaim(Constant.USER_ID, user.getId())
.withClaim(Constant.USER_ROLE, user.getRole())
// 过期时间
.withExpiresAt(new Date(System.currentTimeMillis() + Constant.EXPIRE_TIME))
.sign(algorithm); //签名
return ApiRestResponse.success(token);
}
@Component
public class Constant {
public final static String JWT_KEY="yygs";
public final static String JWT_TOKEN="jwt_token";
public final static String USER_ID="user_id";
public final static String USER_NAME="user_name";
public final static String USER_ROLE="user_role";
public static final Long EXPIRE_TIME=60 * 1000 * 60 * 24 * 1L; //单位时间是毫秒
}
3.过滤器类UserFiter
用来验证用户的第二次登录
package com.yygs.mall.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.yygs.mall.exception.ExceptionEnum;
import com.yygs.mall.exception.MallException;
import com.yygs.mall.model.pojo.User;
import com.yygs.mall.service.UserService;
import com.yygs.mall.utils.Constant;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 描述:
*/
public class UserFilter implements Filter {
public static User currentUser;
@Resource
UserService userService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 获得token
String token = httpServletRequest.getHeader(Constant.JWT_TOKEN);
// 如果token为空,则返回需要TOKEN信息
if (token == null) {
PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) response).getWriter();
out.write("{\n"
+ " \"status\": 10007,\n"
+ " \"msg\": \"NEED_TOKEN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return;
}
// 解析token
Algorithm algorithm = Algorithm.HMAC256(Constant.JWT_KEY);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT jwt = jwtVerifier.verify(token);
// 获得信息
currentUser = new User();
currentUser.setId(jwt.getClaim(Constant.USER_ID).asInt());
currentUser.setRole(jwt.getClaim(Constant.USER_ROLE).asInt());
currentUser.setUsername(jwt.getClaim(Constant.USER_NAME).asString());
}catch (TokenExpiredException e) {
// token过期,抛出自定义的异常
throw new MallException(ExceptionEnum.TOKEN_EXPIRE);
}catch (JWTDecodeException e) {
throw new MallException(ExceptionEnum.TOKEN_WRONG);
}
// 完成过滤
chain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
4.配置过滤器
配置过滤器来过滤需要jwt验证的URL请求
package com.yygs.mall.config;
import com.yygs.mall.filter.AdminFilter;
import com.yygs.mall.filter.UserFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述: User过滤器的配置
*/
@Configuration
public class UserFilterConfig {
@Bean
public UserFilter userFilter(){
return new UserFilter();
}
@Bean(name = "userFilterConf")
public FilterRegistrationBean userFilterConfig() {
FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean();
filterFilterRegistrationBean.setFilter(userFilter());
filterFilterRegistrationBean.addUrlPatterns("/cart/*");
filterFilterRegistrationBean.addUrlPatterns("/order/*");
filterFilterRegistrationBean.addUrlPatterns("/user/update");
filterFilterRegistrationBean.setName("userFilterConfig");
return filterFilterRegistrationBean;
}
}