兄弟们,继昨天的代码之后,继续完成最后的用户模块开发,
昨天已经完成了关于用户的信息编辑页面这些,今天再完善一下,
从后端这边开始吧,做一个拦截器,对用户做身份校验,
拦截器
这边先解释一下吧:
那就再用面试题来细说:
拦截器相关面试题:过滤器与拦截器有什么区别?
答:
- 一:运行的顺序不同
- 过滤器是servlet容器接收到请求之后,在servlet被调用之前运行的
- 拦截器则是在servlet被调用之后,但是在响应被发送到客户端之前运行的
- 二:配置方式不同
- 过滤器是在web.xml配置
- 拦截器是在spring的配置文件中配置,或者基于注解进行配置
- 三:依赖关系
- Filter依赖于Servlet容器,而Interceptor不依赖于Servlet容器
- 四:能力方面
- Filter在过滤器中只能对request和response进行操作
- Interceptor可以对request,response,handler,modelAndView,exception,相当于Interceptor多了对SpringMvc生态下组件的一个操作能力
- 接口规范不同
- 过滤器需要实现Filter接口,拦截器需要实现HandlerInterceptor接口
- 拦截范围不同
- 过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源
OK,来做一个拦截器:
创建一个interceptors包,并创建LoginInterceptor类
拦截器的类要继承
HandlerInterceptor
注:
首先,创建拦截器类时,需要实现HandlerInterceptor接口。这个接口定义了三个方法:preHandle()、postHandle()和afterCompletion()。preHandle()方法是在控制器方法执行之前调用的,它返回一个布尔值,表示是否继续执行后续操作;postHandle()方法是在控制器方法执行之后,视图解析之前调用的,可以用来对响应进行进一步的处理;afterCompletion()方法是在整个请求处理完成之后调用的,通常用于资源的清理工作。
其次,除了实现HandlerInterceptor接口,还可以选择继承HandlerInterceptorAdapter类来简化拦截器的实现。HandlerInterceptorAdapter提供了HandlerInterceptor接口的空实现,使得开发者只需要重写自己关心的方法即可。
最后,为了让拦截器生效,还需要在Spring配置文件中进行相应的配置。这通常涉及到定义一个配置类,实现WebMvcConfigurer接口,并重写addInterceptors()方法。在这个方法中,可以使用addPathPatterns()来指定拦截路径,使用excludePathPatterns()来指定排除的路径。
在实际开发中,根据需求选择合适的拦截器类型是非常重要的。例如,如果需要在Controller层进行权限验证,那么使用HandlerInterceptor接口是合适的;如果只是对请求进行简单的预处理和后处理,那么可以考虑使用WebRequestInterceptor接口。选择合适的拦截器类型可以确保代码的整洁性和可维护性。
下面给出代码
import org.example.cetidenet.utils.JwtUtil; import org.example.cetidenet.utils.ThreadLocalUtil; 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; import java.util.Map; @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
这里继承HandlerInterceptor之后按快捷键Ctrl+o,然后继承三种方法就行了,这里常用的是第一种preHandle,在请求到Controller层之前会被preHandle拦截并处理,如果返回不为true,则会被拦截。
创建了拦截器之后还要注册拦截器。
这里就在之前创建的config包下进行操作
在之前加载Swagger资源的类下加上代码:
@Configuration public class WebConfiguration extends WebMvcConfigurationSupport { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //登录接口和注册接口不拦截 registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login","/user/register","/doc.html#/home"); }
这里有个小坑(之前单独创建了一个类继承的是WebMvcConfigurer,结果拦截器缺没有起作用,如果是这样,就继承WebMvcConfigueationSupport)
添加了拦截的的路径和放行的路径之后就可以进行检验了
OK,这里使用拦截器之后,登录接口可以正常使用,
而使用更新接口则返回401没有权限。那也就成功了。
JWT令牌:
那么使用了拦截器之后拦截了除了登录注册之外的其他的接口,那该如何才能让其使用呢,必然需要验证身份,这里可以使用JWT令牌,
也就是在用户登录完成之后发放JWT令牌,然后反馈给前端,前端携带其到后端,拦截器对其进行校验,如果带有JWT令牌就放行。
后端引入JWT令牌依赖
com.auth0 java-jwt4.4.0 import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import java.util.Date; import java.util.Map; public class JwtUtil { private static final String KEY = "CeTide"; //接收业务数据,生成token并返回 public static String genToken(Map
claims) { return JWT.create() .withClaim("claims", claims) .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 )) .sign(Algorithm.HMAC256(KEY)); } //接收token,验证token,并返回业务数据 public static Map parseToken(String token) { return JWT.require(Algorithm.HMAC256(KEY)) .build() .verify(token) .getClaim("claims") .asMap(); } } 然后在登录接口这里添加代码:
public Result
login(UserLoginDTO userLoginDTO) { //先获取DTO中的账号面膜 String username = userLoginDTO.getUserName(); String password = userLoginDTO.getPassword(); //先查询数据库中是否有这号人物 User user = userMapper.findByUsername(username); //判断是否存在 if(user == null){ return Result.error("该用户不存在"); } String salt = password + "ceTide"; String pwd = DigestUtils.md5Hex(salt.getBytes()).toUpperCase(); //存在,判断密码是否正确 if(!user.getPassword().equals(pwd)){ return Result.error("用户密码错误"); } // boolean isLog = logService.addUserLogin(user); // if(!isLog){ // return Result.error("用户登录日志记录失败"); // } //登录成功 Map claims = new HashMap<>(); claims.put("id", user.getId()); claims.put("username", user.getUserName()); String token = JwtUtil.genToken(claims); //存在且密码正确 return Result.success(token); } 将用户的id和username放入到token中(虽然但是,尽量不要把密码放进去)
然后在拦截器这里进行校验:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //令牌验证 System.out.println("开始验证"); String token = request.getHeader("Authorization"); //验证令牌 try{ Map
claims = JwtUtil.parseToken(token); //放行 return true; }catch (Exception e) { //反馈响应码401 response.setStatus(401); return false; } } 这里如果能够解析就代表拥有JWT令牌,那也就可以放行了
现在回到前端这里:
先看一下修改后的前端代码
{{ userNum.userName }}
编辑资料 设置 文章管理 {{ userNum.attention }} 关注 {{ userNum.fans }} 粉丝 {{ userNum.article }} 文章 个人简介:{{ userSelfIntroduce }} CeTide等级
个人成就
个人动态
用户中心
Footer 编辑个人信息 更换头像 用户名: 性别: 电话: 生日: 城市: 住址: CeTide网ID: 个人简介: 文件操作Upload 在登录之后获取到了JWT值
“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc”
这样一大串字,然后前端将其放置在请求头的Authorization属性中
举个例子
export const userGetInfo= () =>{ return request.get('/user/getInfo',{ headers : { 'Authorization' : 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc', } }); }
此时访问页面,就能得到用户名等信息了
然后就算是成功实现了请求的拦截与用户身份的检验
- Filter在过滤器中只能对request和response进行操作
- 过滤器是在web.xml配置
- 拦截器则是在servlet被调用之后,但是在响应被发送到客户端之前运行的
- 过滤器是servlet容器接收到请求之后,在servlet被调用之前运行的
还没有评论,来说两句吧...