登录校验
会话技术
- 会话:用户打开浏览器,访问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登录
@Autowiredprivate 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 {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//1.获取请求urlString url = request.getRequestURL().toString();//2.判断请求url是否包含login,如果包含,说明是登录操作,放行if(url.contains("login")){filterChain.doFilter(servletRequest,servletResponse);return;}//3.获取请求头中的令牌 tokenString jwt = request.getHeader("token");//4.判断令牌是否存在,如果不存在,返回错误结果,未登录if(!StringUtils.hasLength(jwt)){Result error = Result.error("NOT_LOGIN");//对象--JSONString notLogin = JSONObject.toJSONString(error);response.getWriter().write(notLogin);log.info("令牌不存在");return;}//5.解析token 解析失败返回try { //快捷键ctrl+alt+tJwtUtils.parseJwt(jwt);} catch (Exception e) {e.printStackTrace();Result error = Result.error("NOT_LOGIN");//对象--JSONString 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 {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic 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.获取请求urlString url = request.getRequestURL().toString();//2.判断请求url是否包含login,如果包含,说明是登录操作,放行if(url.contains("login")){return true;}//3.获取请求头中的令牌 tokenString jwt = request.getHeader("token");//4.判断令牌是否存在,如果不存在,返回错误结果,未登录if(!StringUtils.hasLength(jwt)){Result error = Result.error("NOT_LOGIN");//对象--JSONString notLogin = JSONObject.toJSONString(error);response.getWriter().write(notLogin);log.info("令牌不存在");return false;}//5.解析token 解析失败返回try { //快捷键ctrl+alt+tJwtUtils.parseJwt(jwt);} catch (Exception e) {e.printStackTrace();Result error = Result.error("NOT_LOGIN");//对象--JSONString 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("对不起,操作失败,请联系管理员");}
};