登录校验
会话技术
- 会话:用户打开浏览器,访问web服务器的资源,会建立会话,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪技术
-
客户端会话跟踪技术 :cookie
-
服务器会话跟踪技术:session
-
令牌技术
腾讯云HTTP文档
Cookie: HTTP 请求报头包含存储先前通过与所述服务器发送的 HTTP cookies Set-Cookie头。
Set-Cookie: HTTP 响应报头被用于从服务器向用户代理发送 cookie。
cookie基本操作
@RestController
@Slf4j
public class SessionController {
//设置cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("admin","123456"));
return Result.success();
}
//获取cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("admin")){
System.out.println(cookie.getName()+"|"+cookie.getValue());
}
}
return Result.success();
}
}
cookie优缺点
- 优点 :HTTP协议中支持的技术
- 缺点 :移动APP无法使用cookie,不安全,用户可以自己禁用cookie,cookie不能跨域
Session
@GetMapping("/s1")
public Result session1(HttpSession session){
session.setAttribute("admin","123456");
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
Object admin = session.getAttribute("admin");
log.info("value:{}",admin);
return Result.success();
}
- 优点:存储在服务端,安全。
- 缺点: 服务器集群环境无法直接使用、 cookie的缺点
令牌
- 优点:支持PC端、移动端。解决集群环境下的认证问题,减轻服务器存储压力。
- 缺点: 需要自己实现。
JWT令牌
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Map<String, Object> claims =new HashMap<>();
claims.put("id",1);
claims.put("name","朱元璋");
Jwts.builder()
.signWith(SignatureAlgorithm.HS256,"admin")//签名算法
.setClaims(claims)//自定义内容
.setExpiration(new Date(System.currentTimeMillis()+60*60*1000))//设置有效期1h
.compact();
//解析
Map<String, Object> claims = Jwts.parser()
.setSigningKey("admin")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5pyx5YWD55KLIiwiaWQiOjEsImV4cCI6MTcwMTY2MzM1Nn0.4dJvYxwjDYf_IP42cXDad967XZO3qV-EzPJbh8-aIzg")
.getBody();
System.out.println(claims);
JWT登录
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
Emp emp1 = empService.login(emp);
//登录成功
if(emp1 != null){
Map<String, Object> claims = new HashMap<>();
claims.put("id",emp1.getId());
claims.put("username",emp1.getUsername());
String jwt = JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("错误");
}
JwtUtils.java
public class JwtUtils {
private static String signKey = "admin";
private static Long expire = 43200000L;
public static String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,signKey )//签名算法
.setClaims(claims)//自定义内容
.setExpiration(new Date(System.currentTimeMillis() + expire))//设置有效期1h
.compact();
return jwt;
}
public static Claims parseJwt(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
过滤器 Filter
Filter快速入门
1.定义Filter类,实现filter接口
2.类上加上@WebFilter注解,配置拦截资源的路径。引导类加@ServletComponentScan开启servlet组件支持
//放行
filterChain.doFilter(servletRequest,servletResponse);
//放行之后的逻辑
放行之后会回到doFilter中,执行放行之后的逻辑。
过滤器链
登录校验Filter-流程
@WebFilter("/*")
@Slf4j
import com.alibaba.fastjson.JSONObject;
import com.example.demo.pojo.Result;
import com.example.demo.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;
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = request.getRequestURL().toString();
//2.判断请求url是否包含login,如果包含,说明是登录操作,放行
if(url.contains("login")){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//3.获取请求头中的令牌 token
String jwt = request.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果,未登录
if(!StringUtils.hasLength(jwt)){
Result error = Result.error("NOT_LOGIN");
//对象--JSON
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);
log.info("令牌不存在");
return;
}
//5.解析token 解析失败返回
try { //快捷键ctrl+alt+t
JwtUtils.parseJwt(jwt);
} catch (Exception e) {
e.printStackTrace();
Result error = Result.error("NOT_LOGIN");
//对象--JSON
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);
log.info("token解析失败");
return;
}
//6.放行
log.info("放行");
filterChain.doFilter(servletRequest,servletResponse);
}
}
拦截器 Interceptor
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前返回true:放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override//视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
import com.example.demo.filter.LoginCheckInterceptor;
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 {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
}
}
拦截路径
拦截器-执行流程
登录校验Interceptor登录拦截
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前返回true:放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.获取请求url
String url = request.getRequestURL().toString();
//2.判断请求url是否包含login,如果包含,说明是登录操作,放行
if(url.contains("login")){
return true;
}
//3.获取请求头中的令牌 token
String jwt = request.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果,未登录
if(!StringUtils.hasLength(jwt)){
Result error = Result.error("NOT_LOGIN");
//对象--JSON
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);
log.info("令牌不存在");
return false;
}
//5.解析token 解析失败返回
try { //快捷键ctrl+alt+t
JwtUtils.parseJwt(jwt);
} catch (Exception e) {
e.printStackTrace();
Result error = Result.error("NOT_LOGIN");
//对象--JSON
String notLogin = JSONObject.toJSONString(error);
response.getWriter().write(notLogin);
log.info("token解析失败");
return false;
}
//6.放行
log.info("放行");
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override//视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
异常处理
全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result ex(Exception ex) {
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
};