实现登录
@PostMapping("login")
public Result login(@RequestBody TbUser user){
//第一步肯定是先去数据库里查看这个用户是否存在
TbUser userAdmin = userDao.selectUser(user.getUserName(),user.getUserPassword());
//第二步,判断不为空的话,就把想封装的信息放到Map集合里面
if(userAdmin != null){
//自定义信息
Map<String , Object> claims = new HashMap<>();
claims.put("id", userAdmin.getId());
claims.put("userName",userAdmin.getUserName());
claims.put("userPassword",userAdmin.getUserPassword());
//使用JWT工具类,生成身份令牌
String token = JwtUtils.generateJwt(claims);
//然后返回给前端
return Result.ok(token);
}
return Result.ok("账号密码错误");
}
JWT工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "itheima";//签名密钥
private static Long expire = 54600000L; //有效时间
/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)//自定义信息(有效载荷)
.signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
.setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间,毫秒单位
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)//指定签名密钥
.parseClaimsJws(jwt)//指定令牌Token
.getBody();
return claims;
}
}
到这里,登录的操作算是完成了,但是只有登录,没有登录拦截,登录就只算是空有虚表,登录就没有了任何意义
登录拦截
Filter过滤器
登录拦截我这里选用的是Filter过滤器,我们先看一下过滤器的执行流程
过滤器当中我们拦截到了请求之后,如果希望继续访问后面的web资源,就要执行放行操作,放行就是调用 FilterChain对象当中的doFilter()方法,在调用doFilter()这个方法之前所编写的代码属于放行之前的逻辑。
在放行后访问完 web 资源之后还会回到过滤器当中,回到过滤器之后如有需求还可以执行放行之后的逻辑,放行之后的逻辑我们写在doFilter()这行代码之后。
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
@Override //初始化方法, 只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init 初始化方法执行了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("DemoFilter 放行前逻辑.....");
//放行请求
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("DemoFilter 放行后逻辑.....");
}
@Override //销毁方法, 只调用一次
public void destroy() {
System.out.println("destroy 销毁方法执行了");
}
}
执行流程我们搞清楚之后,接下来再来介绍一下过滤器的拦截路径,Filter可以根据需求,配置不同的拦截资源路径:
拦截路径 | urlPatterns值 | 含义 |
---|---|---|
拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
目录拦截 | /emps/* | 访问/emps下的所有资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
过滤器链
什么是过滤器链呢?所谓过滤器链指的是在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。
比如:在我们web服务器当中,定义了两个过滤器,这两个过滤器就形成了一个过滤器链。
而这个链上的过滤器在执行的时候会一个一个的执行,会先执行第一个Filter,放行之后再来执行第二个Filter,如果执行到了最后一个过滤器放行之后,才会访问对应的web资源。
访问完web资源之后,按照我们刚才所介绍的过滤器的执行流程,还会回到过滤器当中来执行过滤器放行后的逻辑,而在执行放行后的逻辑的时候,顺序是反着的。
先要执行过滤器2放行之后的逻辑,再来执行过滤器1放行之后的逻辑,最后在给浏览器响应数据。
登录校验过滤器
-
要进入到后台管理系统,我们必须先完成登录操作,此时就需要访问登录接口login。
-
登录成功之后,我们会在服务端生成一个JWT令牌,并且把JWT令牌返回给前端,前端会将JWT令牌存储下来。
-
在后续的每一次请求当中,都会将JWT令牌携带到服务端,请求到达服务端之后,要想去访问对应的业务功能,此时我们必须先要校验令牌的有效性。
-
对于校验令牌的这一块操作,我们使用登录校验的过滤器,在过滤器当中来校验令牌的有效性。如果令牌是无效的,就响应一个错误的信息,也不会再去放行访问对应的资源了。如果令牌存在,并且它是有效的,此时就会放行去访问对应的web资源,执行相应的业务操作。
登录验证的流程
代码实现
import lombok.Data;
import java.io.Serializable;
@Data
public class Result implements Serializable {
private int code;
private String msg;
private Object data = "";
public Result() {
}
public Result(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(String msg, Object data) {
this.code = 0;
this.msg = msg;
this.data = data;
}
public Result(Object data) {
this.code = 0;
this.msg = "success";
this.data = data;
}
public static Result ok() {
return new Result(0, "success", "");
}
public static Result ok(Object obj) {
return new Result(0, "success", obj);
}
public static Result fail(BusinessStatus businessStatus) {
return fail(businessStatus, businessStatus.getMessage());
}
public static Result notLogin(Object obj){
return new Result(BusinessStatus.NOT_LOGIN.getCode(), BusinessStatus.NOT_LOGIN.getMessage(), obj);
}
public static Result fail(BusinessStatus businessStatus, String msg) {
Result result = new Result();
result.setCode(businessStatus.getCode());
result.setMsg(msg);
return result;
}
}
public enum BusinessStatus {
/**
* 请求成功
*/
OK(0, "success"),
/**
* 未注册
*/
NOT_REGISTER(400, "未注册"),
/**
* 未注册
*/
NOT_FORBIDDEN(400, "用户已被禁用"),
/**
* 未登录或已过期
*/
NOT_LOGIN(401, "未登录或已过期"),
/**
* 管理员 - 账号密码错误
*/
ADMIN_PASSWORD(402, "账号密码错误"),
/**
* 缺少参数
*/
REQUIRE_PARAM(403, "缺少参数"),
/**
* 无访问权限
*/
NO_PERMISSION(404, "无访问权限"),
/**
* 内容存在敏感或非法内容
*/
RISKY_CONTENT(405, "内容存在敏感或非法内容"),
/**
* 验证码错误
*/
ADMIN_CODE(406, "验证码错误"),
/**
* 验证码超时timestamp
*/
ADMIN_TIMESTAMP(407, "验证码超时"),
/**
* 未知错误
*/
UNKNOWN(500, "未知错误"),
/**
* 操作失败
*/
FAIL(500, "操作失败"),
/**
* 资源不存在
*/
RESOURCE_NOT_EXISTS(501, "资源不存在"),
/**
* 资源已存在
*/
RESOURCE_HAS_EXISTS(502, "资源已存在"),
/**
* 需要完善姓名
*/
REQUIRE_NAME(503, "需要完善姓名");
private Integer code;
private String message;
BusinessStatus(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
/**
* 根据状态码查询错误信息
*
* @param code 状态码
* @return message 错误信息
*/
public static String getMessage(int code) {
for (BusinessStatus status : values()) {
if (status.getCode().equals(code)) {
return status.getMessage();
}
}
return null;
}
}
import com.alibaba.fastjson.JSONObject;
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 chain) throws IOException, ServletException {
//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = request.getRequestURL().toString();
log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
if(url.contains("/login")){
chain.doFilter(request, response);//放行请求
return;//结束当前方法的执行
}
//3.获取请求头中的令牌(token)
String token = request.getHeader("token");
log.info("从请求头中获取的令牌:{}",token);
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(token)){
log.info("Token不存在");
Result responseResult = Result.notLogin("请先登录,再操作系统");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("令牌解析失败!");
Result responseResult = Result.notLogin("请先登录,再操作系统");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return;
}
//6.放行
chain.doFilter(request, response);
}
}
以上就是登录验证的代码流程了
这个链接里写的是最详细的,想认真学过滤器和拦截器的,可以去看看这个文章