Spring Boot使用AOP+自定义注解方式校验是否登录,Feign传递Header

891 阅读1分钟

自定义注解 @CheckLogin

Controller

@GetMapping("/users/{id}")
@CheckLogin
public User findById(@PathVariable Integer id) {
    return userMapper.selectByPrimaryKey(id);
}

1. 添加自定义注解接口

/**
 * 自定义注解接口
 */
public @interface CheckLogin {
}

2. 编写aop,实现校验逻辑

import com.example.usercenter.utils.JwtOperator;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * aop
 */
@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CheckLoginAspect {

    private final JwtOperator jwtOperator;

    @Around("@annotation(com.example.usercenter.auth.CheckLogin)")
    public Object checkLogin(ProceedingJoinPoint point) {
        // TODO 在这里编写自己业务的校验逻辑
        try {
            // 1.从header里面获取token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = ((ServletRequestAttributes) requestAttributes);
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("X-Token");
            // 2.校验token是否合法,不合法则直接抛出异常,合法则放行
            Boolean isValid = jwtOperator.validateToken(token);
            if (!isValid) {
                throw new SecurityException("Token不合法");
            }
            // 3. 校验成功,将用户信息设置到request的attribute里面
            Claims claims = jwtOperator.getClaimsFromToken(token);
            request.setAttribute("id", claims.get("id"));
            request.setAttribute("role", claims.get("role"));
            // 校验通过
            return point.proceed();
        } catch (Throwable throwable) {
            // 校验不通过
            throw new SecurityException("Token不合法");
        }
    }
}

3. 添加SecurityException异常捕捉,返回json错误信息

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常捕捉
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionErrorHandler {

    /**
     * 捕捉指定SecurityException异常
     *
     * @param e SecurityException
     * @return ResponseEntity<JsonResult>
     */
    @ExceptionHandler(SecurityException.class)
    public ResponseEntity<JsonResult> error(SecurityException e) {
        log.warn("发生SecurityException异常", e);
        return new ResponseEntity<>(JsonResult.builder()
                .code(HttpStatus.UNAUTHORIZED.value())
                .msg("请登录后再进行访问!")
                .build(),
                HttpStatus.UNAUTHORIZED);
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class JsonResult {
    private Integer code;
    private String msg;
    private Object date;
}

header不添加X-Token调用接口,返回数据:

Feign自定义拦截器传递header

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 自定义拦截器,获取header参数并进行传递
 */
public class TokenRelayRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 1. 获取到token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = ((ServletRequestAttributes) requestAttributes);
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("X-Token");

        // 2.将token传递
        if (StringUtils.isNotBlank(token)) {
            requestTemplate.header("X-Token", token);
        }
    }
}

在配置文件中添加

feign:
  client:
    config:
      # 全局配置
      default:
        # 日志级别(NONE,BASIC,HEADERS,FULL)
        loggerLevel: full
        requestInterceptors:
          - com.example.contentcenter.feignclient.interceptor.TokenRelayRequestInterceptor

将拦截器注册为feign全局配置

这样,feign在调用需要登录的接口时,就会将header中X-Token参数携带过去了