登录校验的实现思路是怎样的?
项目中访问核心资源,通常都要进行登录校验,访问京东商城的订单,必须要先登录才能访问查询
实现思路: 可以在访问资源前进行访问的拦截,判断你当前会话是否有登录,如果没有登录拒绝访问。如果是登录的那就可以进行访问资源。
- 用户输入用户名和密码提交登录请求。
- 服务器接收登录请求并验证用户名和密码的合法性。
- 如果用户名和密码合法,服务器生成一个会话(Session)并将用户的身份信息保存在会话中。(SpringBoot的web环境中,我们要想获取Session会话对象,直接就可以在Controller方法的形参中声明)( public Result login(@RequestBody Emp emp, HttpSession session))
- 服务器将会话ID返回给客户端,通常是通过在浏览器的Cookie中设置会话ID。
- 客户端在后续的请求中将会话ID附加在请求的头部或参数中发送给服务器。
- 服务器接收到请求后,从请求中获取会话ID,并根据会话ID查找对应的会话信息。
- 如果会话存在且有效,服务器认为用户已登录,并继续处理请求;如果会话不存在或已过期,服务器返回错误信息,要求用户重新登录。
- 在用户退出登录或会话过期时,服务器会删除对应的会话信息,客户端需要重新进行登录验证。
会话技术有哪些方式可以实现?
传统技术可以使用cookie+Session去实现会话,进行会话跟踪。但是这种传统方式有弊端: 不能在集群服务中灵活使用,手机客户端无法支持cookie。
为了解决这些问题,可以使用令牌技术来解决。令牌技术优点是:可以在集群服务中灵活使用,移动端也可以支持。
1,Cookie:Cookie存储会话信息,服务器在响应中设置一个包含会话ID的Cookie,客户端在后续的请求中将会话ID通过Cookie发送给服务器。服务器根据会话ID查找对应的会话信息。Cookie的优点是简单易用,但存在一些安全性和跨域限制。
2,Session:使用Token来实现无状态的会话管理。服务器在登录成功后生成一个Token,并将Token返回给客户端。客户端在后续的请求中将Token附加在请求的头部或参数中发送给服务器。服务器根据Token来验证用户身份和权限。Token的优点是无状态、可扩展性强,适用于分布式系统。
- 服务端集群环境下Session的共享问题。
- 移动端APP端无法使用Cookie。
令牌技术
- 使用 json 作为数据传输,有广泛的通用型,并且体积小,便于传输;
- 优点:可以实现无状态的会话管理,服务器不需要存储会话信息,从而提高系统的可扩展性和性能。同时,令牌可以灵活地应用于不同的客户端,如Web应用、移动应用等。
JWT令牌组成部分有哪些,各自作用是什么?
三部分组成:header.payload.signature 部分的数据会以Base64的方式进行编译
第一部分:Header(头),作用:记录令牌类型、签名算法等
第二部分:Payload(有效载荷),作用:携带一些用户信息及过期时间等
第三部分:Signature(签名),作用:防止Token被篡改、确保安全性
怎么使用JWT令牌?(依赖,创建,校验)
1,pom.xml 引入jwt的依赖
2,创建令牌
引入JWT工具类
登录完成后,调用工具类生成JWT令牌并返回
效验
项目中在什么时候去生成令牌?
在登录时生成JWT令牌,在访问核心资源的时候进行令牌效验
当前端携带令牌访问资源时怎么去拦截校验令牌的合法性?
过滤器Filter、拦截器Interceptor。
- 在后端中,实现一个拦截器,用于拦截所有需要进行令牌校验的请求。
- 在拦截器中,获取请求中的令牌(从请求头部、参数或Cookie中获取)。
- 对获取到的令牌进行解密效验,确保令牌的完整性和真实性。
- 如果令牌验证通过,将解密后的令牌中的用户身份信息(如用户ID、角色等)存储在请求对象中,以便后续的处理逻辑使用。
- 如果令牌验证不通过,可以返回错误信息或重定向到登录页面,要求用户重新进行身份验证。
过滤器具体使用的步骤是怎样的?
- 获取请求url(路径)。
- 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
- 如果不包含login,获取请求头中的令牌(token)。
- 判断token是否存在,如果不存在,返回错误结果(未登录)。
- (存在) 解析token,如果解析失败,返回错误结果(未登录)。
- 解析成功则放行。
1,引入json数据处理的依赖.
2,定义登录校验过滤器类,实现 Filter接口,并重写doFilter
方法
3,配置Filter拦截资源的路径:在类上定义 @WebFilter (urlPatterns = "/*")注解
4,在引导类上使用@ServletComponentScan
开启 Servlet 组件扫描
拦截器具体使用的步骤是怎样的?
- 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。
- 注册拦截器,实现WebMvcConfigurer接口,重写addInterceptor方法
项目中异常是怎么处理的?具体怎么实现?
抛出异常:直接抛出异常,然后在Controller的方法中进行try…catch捕获处理
全局异常处理器:
任务:把登录校验功能实现完善,并集成全局异常处理
SpringMVC中提供了全局异常处理器接收所有Controller中产生的异常。一般定义在exception
包下:
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
Filter 与 Interceptor 区别?
- 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
- 拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。
拦截器的拦截路径怎样设置?
过滤器Filter:
登录校验过滤器
@WebFilter(urlPatterns = "/*") public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;String url = request.getRequestURL().toString();//如果是login, 直接放行if(url.contains("login")){System.out.println("登录操作, 直接放行...");filterChain.doFilter(req, res);return;}//如果不是 login ,需要校验 tokenString token = request.getHeader("token");if(!StringUtils.hasLength(token)){ //如果没有JWT令牌System.out.println("获取到token为空 , 返回错误信息...");//返回 未登录 提示信息String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));//response.setContentType("application/json;charset=utf-8");response.getWriter().write(result);return ;}//解析jwt令牌, 如果解析失败, 则说明令牌无效 , 返回 未登录 提示信息try {JwtUtils.parseJWT(token);System.out.println("令牌解析成功, 直接放行 ...");} catch (Exception e) {e.printStackTrace();System.out.println("令牌解析失败 , 返回错误信息...");//返回 未登录 提示信息String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));// response.setContentType("application/json;charset=utf-8");response.getWriter().write(result);return ;}//如果校验通过放行filterChain.doFilter(req, res);}}
拦截器:Interceptor
定义拦截器:
//@Component
public class LoginCheckInterceptor implements HandlerInterceptor {//目标资源方法执行前执行 , true : 放行 ; false : 不放行,拦截 ;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url = request.getRequestURL().toString();//如果是login, 直接放行if(url.contains("login")){System.out.println("登录操作, 直接放行...");return true;}//如果不是 login ,需要校验 tokenString token = request.getHeader("token");if(!StringUtils.hasLength(token)){ //如果没有JWT令牌System.out.println("获取到token为空 , 返回错误信息...");//返回 未登录 提示信息String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));//response.setContentType("application/json;charset=utf-8");response.getWriter().write(result);return false;}//解析jwt令牌, 如果解析失败, 则说明令牌无效 , 返回 未登录 提示信息try {JwtUtils.parseJWT(token);System.out.println("令牌解析成功, 直接放行 ...");} catch (Exception e) {e.printStackTrace();System.out.println("令牌解析失败 , 返回错误信息...");//返回 未登录 提示信息String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));response.setContentType("application/json;charset=utf-8");response.getWriter().write(result);return false;}//如果校验通过放行return true;}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle ....");}//请求处理完成后调用@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion ....");}}
注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");}
}
JWT令牌工具类:
JWT工具类
public class JwtUtils {private static String signKey = "itheima";private static Long expire = 43200000L;/*** 生成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).getBody();return claims;}
}