Browse Source

feat:添加登录拦截器、登录状态保存、异常处理

liuchuanwei 1 month ago
parent
commit
04f116ec54

+ 24 - 0
src/main/java/com/anyway/exception/GlobalExceptionHandler.java

@@ -2,11 +2,13 @@ package com.anyway.exception;
 
 import com.anyway.util.R;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
 import org.springframework.validation.ObjectError;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
 
 import java.util.stream.Collectors;
 
@@ -21,14 +23,36 @@ import java.util.stream.Collectors;
 public class GlobalExceptionHandler {
     @ExceptionHandler(MethodArgumentNotValidException.class)
     @ResponseBody
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
     public R<Void> handleValidException(MethodArgumentNotValidException e){
         log.error("参数校验失败:{}", e.getMessage(), e);
         String errorMessages = e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(";"));
         return R.fail(errorMessages);
     }
 
+    /**
+     * 登录异常
+     *
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(LoginException.class)
+    @ResponseBody
+    @ResponseStatus(HttpStatus.UNAUTHORIZED)
+    public R<Void> handleLoginException(LoginException e){
+        log.error("登录失败:{}", e.getMessage(), e);
+        return R.fail("请登录");
+    }
+
+    /**
+     * 处理不可预知的异常
+     *
+     * @param e
+     * @return
+     */
     @ExceptionHandler(RuntimeException.class)
     @ResponseBody
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
     public R<Void> handleRuntimeException(RuntimeException e){
         log.error("系统异常:{}", e.getMessage(), e);
         return R.fail("系统异常:" + e.getMessage());

+ 18 - 0
src/main/java/com/anyway/exception/LoginException.java

@@ -0,0 +1,18 @@
+package com.anyway.exception;
+
+/**
+ * 业务异常
+ */
+public class LoginException extends RuntimeException{
+
+    private String message;
+
+    public LoginException(String message) {
+        this.message = message;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+}

+ 11 - 2
src/main/java/com/anyway/favor/controller/UserController.java

@@ -2,12 +2,15 @@ package com.anyway.favor.controller;
 
 import com.anyway.favor.model.User;
 import com.anyway.favor.service.UserService;
+import com.anyway.util.JwtUtils;
 import com.anyway.util.PageQuery;
 import com.anyway.util.R;
+import com.google.common.collect.ImmutableMap;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -28,10 +31,16 @@ public class UserController {
 
 
     @PostMapping("/login")
-    public R<Void> login(@RequestBody User user) {
+    public R<Void> login(@RequestBody User user, HttpServletResponse response) {
         log.info("登录表单:{}", user);
         User loginUser = userService.findByUserName(user.getUserName());
-        log.info("登录用户:{}", loginUser);
+        if (loginUser == null || !loginUser.getPassword().equals(user.getPassword())) {
+            return R.fail("用户名或密码错误");
+        }
+        //生成token并返回
+        Map<String, Object> map = ImmutableMap.of("userName", loginUser.getUserName());
+        String token = JwtUtils.createToken(map);
+        response.setHeader(JwtUtils.TOKEN_HEADER, token);
         return R.ok();
     }
 

+ 72 - 0
src/main/java/com/anyway/favor/interceptor/LoginInterceptor.java

@@ -0,0 +1,72 @@
+package com.anyway.favor.interceptor;
+
+import com.anyway.exception.LoginException;
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.UserService;
+import com.anyway.util.JwtUtils;
+import com.anyway.util.SessionUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * 登录拦截器
+ *
+ * @author liuchuanwei
+ * @date 2024-08-16
+ */
+@Slf4j
+public class LoginInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        SessionUtils.clearCurrentUser();
+        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String token = request.getHeader(JwtUtils.TOKEN_HEADER);
+        if (token != null) {
+            //校验
+            int tokenVerifyResult = JwtUtils.verifyToken(token);
+            if (tokenVerifyResult == 0) {
+                Map<String, Object> map = JwtUtils.parseToken(token);
+                if (map != null) {
+                    //校验通过
+                    //获取用户信息
+                    String userName = (String) map.get("userName");
+                    //从数据库中查询用户信息
+                    User user = userService.findByUserName(userName);
+                    //将用户信息放入session中
+                    SessionUtils.putCurrentUser(user);
+                    return true;
+                }
+            } else if (tokenVerifyResult == 1) {
+                log.info("token过期");
+            } else if (tokenVerifyResult == 2) {
+                log.info("token无效");
+            } else if (tokenVerifyResult == 3) {
+                log.info("token解析失败");
+            } else if (tokenVerifyResult == 4) {
+                log.info("token签名错误");
+            } else if (tokenVerifyResult == 5) {
+                log.info("token非法参数");
+            }
+        }
+        throw new LoginException("请登录");
+    }
+}

+ 11 - 4
src/main/java/com/anyway/util/SessionUtils.java

@@ -1,7 +1,6 @@
 package com.anyway.util;
 
 import com.anyway.favor.model.User;
-import com.anyway.favor.service.UserService;
 
 /**
  * Session 工具类
@@ -10,15 +9,15 @@ import com.anyway.favor.service.UserService;
  * @date 2024-02-21
  */
 public class SessionUtils {
+
+    private static ThreadLocal<User> currentUser = new ThreadLocal<>();
     /**
      * 获取当前登录用户
      *
      * @return
      */
     public static User currentUser() {
-        //TODO 暂时固定用户
-        UserService userService = SpringUtils.getBean("userServiceImpl");
-        return userService.findById(1L);
+        return currentUser.get();
     }
     /**
      * 获取当前登录用户ID
@@ -29,4 +28,12 @@ public class SessionUtils {
         User currentUser = SessionUtils.currentUser();
         return currentUser.getId();
     }
+
+    public static void putCurrentUser(User user) {
+        currentUser.set(user);
+    }
+
+    public static void clearCurrentUser() {
+        currentUser.remove();
+    }
 }

+ 10 - 1
src/main/webapp/WEB-INF/spring-servlet.xml

@@ -24,9 +24,18 @@
 	</mvc:annotation-driven>
 	<!-- 设置使用注解的类所在的jar包 -->
 	<context:component-scan base-package="com.anyway.favor.**.controller"/>
+	<!-- 拦截器 -->
+	<mvc:interceptors>
+		<!-- 登录拦截器 -->
+		<mvc:interceptor>
+			<mvc:mapping path="/**"/>
+			<mvc:exclude-mapping path="/user/login"/>
+			<bean class="com.anyway.favor.interceptor.LoginInterceptor" />
+		</mvc:interceptor>
+	</mvc:interceptors>
 	<!-- 资源映射,因为spring mvc接受了所有的请求,所以要在这个地方设置一下资源 -->
 	<mvc:resources mapping="/asserts/**" location="/asserts/"/>
 	<!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
 	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/pages/" p:suffix=".jsp" />
 
-</beans>
+</beans>