SpringBoot简单的全局异常处理和登录拦截

前言

敲代码的时候会遇到各种各样的异常,有业务异常,有代码异常,如果都用try-catch处理,那真的是要了命了。所以,我们就想个办法来全局处理异常。下面就是一个简单的思路,实际应用的时候可以基于此优化。

一、定义统一返回数据

为了和前端交流更加方便,定义一个类用来返回前端。

@Data
public class Result<T> implements Serializable {

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 提示信息
     */
    private String msg;

    /**
     * 返回的具体内容
     */
    private T data;

    private Result(){}

    private Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> Result<T> success(){
        return success(null);
    }

    public static <T> Result<T> success(T data){
        return success("success", data);
    }

    public static <T> Result<T> success(String msg, T data){
        return success(200, msg, data);
    }

    /**
     * 成功时调用
     *
     * @param  code 错误码
     * @param  msg 返回信息
     * @param  data 返回数据
     * @return {@link Result<T>}
     */
    public static <T> Result<T> success(Integer code, String msg, T data){
        return new Result<>(code, msg, data);
    }

    public static Result<Object> error(String msg){
        return error(500, msg, null);
    }

    public static Result<Object> error(Integer code, String msg){
        return error(code, msg, null);
    }

    /**
     * 失败
     *
     * @param  code 错误码
     * @param  msg 错误信息
     * @param  cause 异常信息
     * @return {@link Result}
     */
    public static Result<Object> error(Integer code, String msg, Object cause){
        Assert.notNull(code, "错误码不能为空");
        Assert.notNull(msg, "错误信息不能为空");
        msg = msg.trim();
        return new Result<>(code, msg, cause);
    }

}

二、自定义异常

自定义一个异常类,出现业务异常的时候可以抛出此异常

public class CustomException extends RuntimeException{

    private int code;

    public void setCode(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public CustomException(int code, String message) {
        super(message);
        this.code = code;
    }

}

三、全局抓取异常

使用@RestControllerAdvice注解和@ExceptionHandler注解来全局抓取异常,然后进行相应的处理,怎么处理就看自己的业务了。
@RestControllerAdvice
public class CustomExceptionHandler {

    /**
     * 全局异常处理
     *
     * @param exception  异常
     * @return Object
     */
    @ExceptionHandler(value = Exception.class)
    public Object exceptionHandler(Exception exception) {
        if (exception instanceof CustomException){
            CustomException customException = (CustomException) exception;
            return Result.error(customException.getCode(), customException.getMessage(), customException.getCause());
        }else {
            String stacktrace = ExceptionUtil.getMessage(exception);
            return Result.error(stacktrace);
        }
    }

}

四、登录拦截

1、定义一个注解用于忽略认证

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
}

2、定义一个认证拦截器,拦截未登录的请求

public class AuthenticationInterceptor implements HandlerInterceptor {

    private static final String OPTIONS = "OPTIONS";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 放行options请求
        if (request.getMethod().toUpperCase().equals(OPTIONS)) {
            passOptions(response);
            return false;
        }
        Method method = null;
        if (handler instanceof HandlerMethod) {
            method = ((HandlerMethod) handler).getMethod();
        }
        // 检查是否忽略权限验证
        if (method == null || checkIgnore(method)) {
            return true;
        }

        // 获取token
        String accessToken = takeToken(request);
        if (StrUtil.isBlank(accessToken)) {
            throw new CustomException(1001, "Token不能为空");
        }

        // 解析token
        String username = parseToken(accessToken);
        HttpSession session = request.getSession();
        String tokenInSession = (String) session.getAttribute(username);
        if (StrUtil.isBlank(tokenInSession)){
            throw new CustomException(1002, "token错误");
        }
        if (!StrUtil.equals(accessToken, tokenInSession)){
            throw new CustomException(1002, "token错误");
        }
        return true;
    }

    /**
     * 放行options请求
     */
    public static void passOptions(HttpServletResponse response) {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, X-Custom-Header, Authorization");
    }

    /**
     * 检查是否忽略权限
     */
    public static boolean checkIgnore(Method method) {
        Ignore annotation = method.getAnnotation(Ignore.class);
        // 方法上没有注解再检查类上面有没有注解
        if (annotation == null) {
            annotation = method.getDeclaringClass().getAnnotation(Ignore.class);
            return annotation != null;
        }
        return true;
    }

    /**
     * 取出前端传递的token
     */
    public static String takeToken(HttpServletRequest request) {
        String accessToken = request.getParameter("accessToken");
        if (StrUtil.isBlank(accessToken)) {
            accessToken = request.getHeader("Authorization");
        }
        return accessToken;
    }

    public static String parseToken(String accessToken){
        String[] token = accessToken.split("--");
        return token[0];
    }
}

3、登录

@RestController
@RequestMapping("/api/v1/system")
public class LoginController {

    @Autowired
    private HttpServletRequest request;

    @Ignore
    @GetMapping("/login")
    public Result login(String username, String password){
        HttpSession session = request.getSession();
        String sessionId = session.getId();
        String token = username + "--" + sessionId;
        session.setAttribute(username, token);

        Map<String, Object> map = new HashMap<>(1);
        map.put("token", token);
        return Result.success(map);
    }

    @GetMapping("/info")
    public Result getUserInfo(){
        Map<String, Object> userInfo = new HashMap<>(2);

        Map<String, Object> user = new HashMap<>(1);
        user.put("nickname", "1234");
        userInfo.put("user", user);

        Set<String> roles = new HashSet<>();
        roles.add("admin");
        userInfo.put("roles", roles);

        return Result.success(userInfo);
    }

}

写在最后的话

这篇文章只是一个简单的思路,稍微修改一下就可以完成简单的认证。复杂的认证和授权可以使用springSecurity实现。

猜你喜欢

转载自blog.csdn.net/WayneLee0809/article/details/114028084