目录
一、概述
没有登录校验的话,即使我们没有登录,也能访问功能网页。
解决方法是加上登录标记和统一拦截功能
二、会话技术
1、什么是会话技术
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 会话跟踪方案:
- 客户端会话跟踪技术:Cookie(传统)
- 服务端会话跟踪技术:Session(传统)
- 令牌技术(主流)
2、会话跟踪方案对比
方案一:Cookie
注:什么叫跨域
三个维度任何一个不同就不是相同的域
方案二:Session
方案三:令牌技术
三、JWT令牌技术
1、简介
- 全称:JSON Web Token(https://jwt.io/)
- 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
- 组成:
- 第一部分:Header(头),记录令牌类型、签名算法等。例如:{"alg":"HS256","type":"JWT"}
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom"}
- 第三部分:Signature(签名),防止Token被簒改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
2、应用场景
- 场景:登录认证
- 登录成功后,生成令牌
- 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
3、JWT的生成
/**
* 生成JWT
*/
@Test
public void testGenJwt(){
Map<String, Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("name","tom");
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "iknow")//算法签名
.setClaims(claims)//自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期为1h
.compact();
System.out.println(jwt);
}
/**
* 解析JWT
*/
@Test
public void testParseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("iknow")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY4NzYwNzQ4Mn0.ytrShABndc41ZCcVOdeoGwbSsXhQIGPFHsk5l8qATJg")
.getBody();
System.out.println(claims);
}
注意事项
- JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
- 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法。
思路
- 令牌生成:登录成功后,生成JWT令牌,并返回给前端。
- 令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验。
4、生成令牌
步骤
- 引入JWT令牌操作工具类。
- 登录完成后,调用工具类生成JWT令牌,并返回。
四、过滤器Filter
1、概念
- 概念:Filter过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
- 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
2、Filter快速入门
步骤
- 定义Filter:定义一个类,实现 Filter接口,并重写其所有方法。
- 配置Filter:Filter类上加@WebFilter注解,配置拦截资源的路径。引导类上加@ServletComponentScan开启Servlet组件支持。
3、Filter详解
Filter执行流程
疑问
- 放行后访问对应资源,资源访问完成后,还会回到Filter中吗?会
- 如果回到Filter中,是重新执行还是执行放行后的逻辑呢?执行放行后逻辑
Filter拦截路径
- Filter 可以根据需求,配置不同的拦截资源路径:
过滤器链
- 介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
- 顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。
4、登录校验Filter
流程
package com.iknow.tliaswebmanagement.filter;
import com.alibaba.fastjson.JSONObject;
import com.iknow.tliaswebmanagement.pojo.Result;
import com.iknow.tliaswebmanagement.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
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 --------------> 阿里巴巴fastJSON
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 --------------> 阿里巴巴fastJSON
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//6.放行
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest, servletResponse);
}
}
五、拦截器Interceptor
1、概述
- 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
- 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
2、Interceptor快速入门
步骤
- 定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。
- 注册拦截器
3、Interceptor详解
拦截器-拦截路径
- 拦截器可以根据需求,配置不同的拦截路径:
拦截器-执行流程
4、登录校验Interceptor
package com.iknow.tliaswebmanagement.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.iknow.tliaswebmanagement.pojo.Result;
import com.iknow.tliaswebmanagement.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前运行,返回true;放行,放回false,不放行
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 --------------> 阿里巴巴fastJSON
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 --------------> 阿里巴巴fastJSON
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//6.放行
log.info("令牌合法,放行");
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle ...");
}
@Override //视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion ...");
}
}