Java学习--JWT令牌
https://jwt.io/
为了防止在未登录情况下,用户也能查看信息,我们引入会话跟踪
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
一、客户端会话跟踪技术:Cookie
优点:HTTP协议中支持的技术
缺点: 移动端APP无法使用Cookie;不安全,用户可以自己禁用Cookie;Cookie不能跨域
二、服务端会话跟踪技术:Session
优点:存储在服务端,安全
缺点:服务器集群环境下无法直接使用Session;Cookie的缺点
三、令牌技术
优点:支持PC端、移动端;解决集群环境下的认证问题;减轻服务器端存储压力
缺点:需要自己实现
3.1 JWT
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
组成:
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷)携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
3.2 JWT生成
引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
记得引入:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
登录成功返回jwt
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info("员工登录:{}",emp);
Emp e = empService.login(emp);
//生成令牌
if(e != null ){
Map<String, Object> claims = new HashMap<>();
claims.put("id",e.getId());
claims.put("name",e.getName());
claims.put("username",e.getUsername());
String jwt = JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("用户名或密码错误");
}
浏览器会存储jwt,往后每次请求都会加上jwt;
3.3 过滤器Filter
3.3.1概念
Filter 过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
3.3.2 配置Filter
1.定义Filter:定义一个类,实现 Filter 接口,并重写其所有方法。
2.配置Filter:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支
@WebFilter(urlPatterns = "/*")//拦截所有请求
public class DemoFilter implements Filter {
@Override//初始化方法,只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override//拦截到请求之后调用,调用多次
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("拦截到请求");
//发行操作
filterChain.doFilter(servletRequest,servletResponse);
}
@Override//销毁方法,只调用一次
public void destroy() {
Filter.super.destroy();
}
}
@ServletComponentScan//开启了对servlet组件支持
@SpringBootApplication
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
3.3.3 过滤器链
一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
3.3.4 登录校验
代码实现如下:
使用fastJson将字符串转换为Json格式,注意配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
@Override//拦截到请求之后调用,调用多次
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("拦截到请求");
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = req.getRequestURL().toString();
log.info("请求的url:{}",url);
//2.判断请求url是否含login,如果包含,放行
if(url.contains("login")){
log.info("登录操作,放行。。。");
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//3.获取请求头中的令牌token
String jwt = req.getHeader("token");
//4.判断令牌是否存在,不存在的时候
if(!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登录信息");
Result error = Result.error("NOT_LOGIN");
//将对象转换成json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//5.解析token,如果解析失败,返回错误信息(未登录)
try{
JwtUtils.parseJWT(jwt);
}catch (Exception e){
e.printStackTrace();
log.info("解析令牌失败,返回未登录错误信息");
Result error = Result.error("NOT_LOGIN");
//将对象转换成json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//6.令牌合法放行
log.info("令牌合法放行");
filterChain.doFilter(servletRequest,servletResponse);
}
3.4 拦截器(Interceptor)
3.4.1 概念
概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
3.4.2 注册拦截器
1.定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。
2.注册拦截器
拦截器可以根据需求,配置不同的拦截路径
3.4.3 登录校验
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//1.获取请求url
String url = req.getRequestURL().toString();
log.info("请求的url:{}",url);
//2.判断请求url是否含login,如果包含,放行
if(url.contains("login")){
log.info("登录操作,放行。。。");
return true;
}
//3.获取请求头中的令牌token
String jwt = req.getHeader("token");
//4.判断令牌是否存在,不存在的时候
if(!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登录信息");
Result error = Result.error("NOT_LOGIN");
//将对象转换成json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//5.解析token,如果解析失败,返回错误信息(未登录)
try{
JwtUtils.parseJWT(jwt);
}catch (Exception e){
e.printStackTrace();
log.info("解析令牌失败,返回未登录错误信息");
Result error = Result.error("NOT_LOGIN");
//将对象转换成json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//6.令牌合法放行
log.info("令牌合法放行");
return HandlerInterceptor.super.preHandle(req, resp, handler);
}