1 登录校验:

2 会话技术:
- 会话:浏览器访问资源,建立会话;一方断开连接,结束会话。一次会话可以包含多次请求和响应。
- 会话跟踪:维护浏览器状态,服务器需要识别多次请求是否来自于同一个浏览器,以便在同一次会话的多次请求间共享数据。
- 会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 优点:HTTP协议自带且支持
- 缺点:
- 移动端APP无法使用
- 不安全
- 不能跨域
- 服务端会话跟踪技术:Session(底层基于Cookie实现)
- 优点:存储在服务端,安全
- 缺点:
- 服务器集群环境下无法使用
- Cookie的缺点
- 令牌技术(主流方案)
- 优点:
- 跨平台通用
- 集群环境适用
- 减轻服务器端存储压力
- 缺点:
- 需要自己实现
- 优点:
- 客户端会话跟踪技术:Cookie
3 JWT令牌
JSON Web Token
组成:
- 一:Header 头,记录令牌类型,签名算法。
- 二:Payload 有效载荷,携带一些自定义消息、默认信息
- 三:Signature 签名,防止Token被篡改、确保安全性。
场景:登录认证
- 登陆成功后,生成令牌
- 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,在处理
生成:
引入依赖:
<!--JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--JAVA8以后JWT需要:-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
引入工具类
生成:
String jwtstr = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "privatekey") // 签名算法、秘钥
.setClaims(claims) // 自定义内容(载荷)Map或Claims
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 设置有效期为1h
.compact();
校验
Claims claims = Jwts.parser()
.setSigningKey("privatekey") //指定签名密钥,与生成时一致
.parseClaimsJws("eyjhbGCiOjI1Ni9.eyJpZC6MSwiXhwiXjoxNjuSOtK1NTE3LCJ1c2VybmFtZSI6IlRvbSI9.EUFTeqPKGslekdBeczcWCe7a7xb
cllwB1MXllcTMwo") // 解析令牌
.getBody();
校验报错则说明被篡改、失效
示例:
public class LoginController {
@Autowired
private EmpService empService;
@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.getName());
String jwt= JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("用户名或密码错误");
}
}
4 过滤器Filter
注意在javax.servlet包下
概述:
- JavaWeb三大组件(Servlet、Filter、Listener )之一
- 可以拦截对资源的请求
- 一般通用于:登录校验、统一编码处理、敏感字符处理…
要使filter生效:
- 给filter加上
@WebFilter,实现Filter接口 - 给启动类加上 @ServletComponentScan
执行流程:
- 浏览器请求进入Fliter,执行放行前逻辑
- 放行:chain.doFilter(request,response);进入对web资源的访问
- 回到Filter,执行放行后逻辑。
拦截路径:
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
// Filter implementation
}
| 拦截路径 | urlPatterns值 | 含义 |
|---|---|---|
| 拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
| 目录拦截 | /emps/* | 访问 /emps 下的所有资源,都会被拦截 |
| 拦截所有 | /* | 访问所有资源,都会被拦截 |
过滤器链:
一个Web应用中可以配置多个过滤器,多个过滤器就形成了一个过滤器链。
过滤器执行顺序:按照类名字典序
登录校验Filter:
LoginFilter.java:
package org.example.ssmpratice1.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.ssmpratice1.pojo.Result;
import org.example.ssmpratice1.utils.JwtUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.http.HttpRequest;
@Slf4j
@WebFilter(urlPatterns = "/*")//拦截所有请求
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1.获取请求URL。
HttpServletRequest httpServletRequest=(HttpServletRequest) servletRequest;
String url = httpServletRequest.getRequestURL().toString();
log.info("url:{}",url);
//2.判断请求URL中是否包含"login",如果包含,说明是登录操作,放行。
if(url.contains("login")){
log.info("放行login");
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//3.获取请求头中的令牌(token)。
String jwt = httpServletRequest.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
if (jwt==null || jwt==""){
log.info("jwt为空,返回error");
Result error=Result.error("NOT_LOGIN");
//因为filter不是control,没有@RestController,所以将Result返回为JSON对象需要手动转换,此处使用fastJSON
String notLogin= JSON.toJSONString(error);
servletResponse.getWriter().write(notLogin);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)。
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("解析令牌失败");
Result error=Result.error("NOT_LOGIN");
//因为filter不是control,没有@RestController,所以将Result返回为JSON对象需要手动转换,此处使用fastJSON
String notLogin= JSON.toJSONString(error);
servletResponse.getWriter().write(notLogin);
return;
}
//6.放行。
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest,servletResponse);
}
}
5 拦截器Interceptor
概述:
由spring提供,动态拦截方法,类似于过滤器。
使用步骤:
1.定义拦截器,实现HandlerInterceptor接口,重写所有方法:
- preHandle() 在目标资源方法执行前执行,返回true,放行;返回false,不放行
- postHandle() 在目标资源方法执行后执行
- afterCompletion() 视图渲染完毕后执行,最后执行
2.注册拦截器
拦截路径:
拦截器可以配置拦截哪些,不拦截哪些。
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");//拦截除/login外的所有访问
}
| 拦截路径 | 含义 | 举例 |
|---|---|---|
| / * | 一级路径 | 能匹配 /depts,/emps,/login,不能匹配 /depts/1 |
| / ** | 任意级路径 | 能匹配 /depts,/depts/1,/depts/1/2 |
| /depts/* | /depts 下的一级路径 | 能匹配 /depts/1,不能匹配 /depts/1/2,/depts |
| /depts/** | /depts 下的任意级路径 | 能匹配 /depts,/depts/1,/depts/1/2,不能匹配 /emps/1 |
执行流程:
请求先到过滤器Filter(如果有),再到拦截器Interceptor:
preHandle :判断是否放行:是->访问操作->postHandle->afterCompletion->回到filter(如果有)
二者比较:
- 接口实现不同:
- filter:Filter;
- interceptor:HandlerIntercrptor
- 拦截范围不同:
- Filter:可以拦截所有资源
- interceptor:只能拦截Spring环境中的资源
