后端学习笔记--登录认证
基础实现
本质就是查询语句
控制器
@Slf4j
@RequestMapping("/login")
@RestController
public class LoginController {@Autowiredprivate EmpService empService;@PostMappingpublic Result login(@RequestBody Emp emp){log.info("员工登录:{}", emp);LoginInfo loginInfo = empService.login(emp);if(loginInfo == null){return Result.error("用户名或密码错误");}return Result.success(loginInfo);}
}逻辑层
@Overridepublic LoginInfo login(Emp emp) {//1.调用mapper接口,根据用户名和密码查询员工Emp e = empMapper.selectByUsernameAndPassword(emp);//2.判断员工是否为空if(e!= null){log.info("登录成功,员工信息:{}", e);return new LoginInfo(e.getId(),e.getUsername(),"",e.getName());}//3.不存在返回NULLreturn null;}
/*** 根据用户名和密码查询员工* @param emp* @return*/@Select("select id,username,name from emp where username = #{username} and password = #{password}")Emp selectByUsernameAndPassword(Emp emp);登录校验

会话技术

方案一 Cookie
//设置Cookie@GetMapping("/c1")public Result cookie1(HttpServletResponse response){response.addCookie(new Cookie("login_username","FeiTwnd")); //设置Cookie/响应Cookiereturn Result.success();}//获取Cookie@GetMapping("/c2")public Result cookie2(HttpServletRequest request){Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if(cookie.getName().equals("login_username")){System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie}}return Result.success();}
方案二 Session
@GetMapping("/s1")public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom"); //往session中存储数据return Result.success();}@GetMapping("/s2")public Result session2(HttpSession session){log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser"); //从session中获取数据log.info("loginUser: {}", loginUser);return Result.success(loginUser);}
方案三 令牌(主流方案)

JWT令牌
介绍

令牌的生成和解析

生成令牌
工具类
package cc.feitwnd.utils;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 = "RmVpVHduZA=="; // Base64编码的签名密钥private static Long expire = (long) (12 * 60 * 60 * 1000);/*** 生成JWT令牌* @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).getBody();return claims;}
}
逻辑层
@Overridepublic LoginInfo login(Emp emp) {//1.调用mapper接口,根据用户名和密码查询员工Emp e = empMapper.selectByUsernameAndPassword(emp);//2.判断员工是否为空if(e!= null){log.info("登录成功,员工信息:{}", e);Map<String,Object> claims = new HashMap<>();claims.put("id",e.getId());claims.put("username",e.getUsername());String jwt = JwtUtils.generateJwt(claims);return new LoginInfo(e.getId(),e.getUsername(),jwt,e.getName());}//3.不存在返回NULLreturn null;}过滤器Filter
介绍


package cc.feitwnd.filter;import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;@Slf4j
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("DemoFilter init...初始化方法运行");}//拦截到请求之后执行,会执行多次@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("DemoFilter doFilter...执行方法运行");}@Overridepublic void destroy() {log.info("DemoFilter destroy...销毁方法运行");}
}
令牌校验Filter

令牌校验Filter-流程

package cc.feitwnd.filter;import cc.feitwnd.utils.JwtUtils;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;@Slf4j
@WebFilter(urlPatterns = "/*")
public class TokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//1.获取请求路径String requestURI = request.getRequestURI();//2.判断是否为登录请求,是则放行if(requestURI.contains("login")){log.info("登录请求,放行");filterChain.doFilter(request,response);return;}//3.获取tokenString token = request.getHeader("token");log.info("token:{}",token);//4.判断token是否存在,不存在返回401状态码if(token == null || token.isEmpty()){log.info("token不存在,返回401状态码");response.setStatus(401);return;}//5.如果token存在,校验tokentry{JwtUtils.parseJWT(token);}catch (Exception e){log.info("token无效,返回401状态码");response.setStatus(401);return;}//6.放行log.info("token有效,放行");filterChain.doFilter(request,response);}
}
Filter-详解

拦截路径

过滤器链

按照文件名来决定执行顺序
拦截器Interceptor

入门程序
1.定义拦截器
package cc.feitwnd.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;@Slf4j
@Component
public class DemoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle");}//视图渲染之后执行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
注册拦截器
package cc.feitwnd.config;import cc.feitwnd.interceptor.DemoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 配置类*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate DemoInterceptor demoInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(demoInterceptor).addPathPatterns("/**");}
}
基于拦截器实现令牌校验功能
package cc.feitwnd.interceptor;import cc.feitwnd.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求路径String requestURI = request.getRequestURI();//2.判断是否为登录请求,是则放行if(requestURI.contains("login")){log.info("登录请求,放行");return true;}//3.获取tokenString token = request.getHeader("token");log.info("token:{}",token);//4.判断token是否存在,不存在返回401状态码if(token == null || token.isEmpty()){log.info("token不存在,返回401状态码");response.setStatus(401);return false;}//5.如果token存在,校验tokentry{JwtUtils.parseJWT(token);}catch (Exception e){log.info("token无效,返回401状态码");response.setStatus(401);return false;}//6.放行log.info("token有效,放行");return true;}
}
拦截路径

执行流程

