使用Spring AOP、自定义注解实现对Request传参拦截

使用Spring boot开发项目与前端人员对接接口时由于在调试,经常传来null、undefined、NaN等非法参数,于是利用spring 的自定义注解以及AOP实现简单的传入参数拦截。

一、使用方法

    /**
     * 登录
     * 当参数是实体类时,通过 参数名.属性名 的方式注解参数
     */
    @ArgsNotNull({"user.stuNo", "user.password"})
    @PostMapping("login.do")
    public ServerResponse login(User user) {
        //To do Login
        return ServerResponse.createBySuccess();
    }
    /**
     * 通过userId查询
     * 当参数是基本类时,通过 参数名 的方式注解参数
     */
    @ArgsNotNull({"userId"})
    @GetMapping("findByUserId.do")
    public ServerResponse findByUserId(String userId) {
        //To do findByUserId
       return ServerResponse.createBySuccess();
    }

二、构建方法
1、添加Spring AOP pom依赖

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>

2、自定义参数检查类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 检查参数注解
 *
 * @author chenyi
 * Create on 2019/10/13
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ArgsNotNull {
    /**
     * 拦截字段
     */
    String[] value() default {};
}

3.AOP类,拦截使用了@ArgsNotNull 注解的方法

/**
 * 参数检验aop
 *
 * @author chenyi
 * Create on 2019/10/13
 */
@Aspect
@Component
public class ArgsNotNullAop {
    /**
     * 检查参数是否为空
     */
    @Around("@annotation(ArgsNotNull)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //拦截的方法
        Method method = methodSignature.getMethod();
        //拦截的方法的注解
        ArgsNotNull argsNotNull = method.getAnnotation(ArgsNotNull.class);
        //拦截的参数名称
        String[] parameters = methodSignature.getParameterNames();
        // 拦截的参数值
        Object[] args = joinPoint.getArgs();
        //注解的参数
        String[] needParams = argsNotNull.value();
        if (doCheckParams(needParams, parameters, args)) {
            return joinPoint.proceed();
        }
        return ServerResponse.createByWarningMsg("非法请求");
    }

    /**
     * 检查注解当中的参数
     *
     * @param needParams 注解中的参数,需要检查的参数
     * @param parameters 方法中传入的参数名称集合
     * @param args       方法中传入的参数集合
     */
    private Boolean doCheckParams(String[] needParams, String[] parameters, Object[] args) {
        for (String needParam : needParams) {
            if (needParam.contains(".") ? isWarpTypeNull(needParam, parameters, args) : isBasicTypeNull(needParam, parameters, args))
                return false;
        }
        return true;
    }

    /**
     * 检查基本类型参数
     * true 参数不为空,通过
     * false 参数为空,不通过
     *
     * @param param      需要检查的参数
     * @param parameters 方法的参数名称集合
     * @param args       方法的参数集合
     */
    private Boolean isBasicTypeNull(String param, String[] parameters, Object[] args) {
        int index = getParamIndex(param, parameters);
        return index >= 0 && args[index] == null;
    }

    /**
     * 检查非基本类型参数
     * true 参数不为空,通过
     * false 参数为空,不通过
     *
     * @param param      需要检查的参数
     * @param parameters 方法的参数名称集合
     * @param args       方法的参数集合
     */
    private Boolean isWarpTypeNull(String param, String[] parameters, Object[] args) {
        String[] split = param.split("\\.");
        int index = getParamIndex(split[0], parameters);
        if (index < 0)
            return true;
        //分解参数类型
        Field[] declaredFields = args[index].getClass().getDeclaredFields();
        try {
            for (Field field : declaredFields) {
                String fieldName = field.getName();//字段名称
                if (fieldName.equals(split[1])) {
                    field.setAccessible(true);//获取private成员变量
                    return field.get(args[index]) == null;
                }
            }
        } catch (IllegalAccessException e) {
            return true;
        }
        return false;
    }

    /**
     * 获取参数位置索引
     *
     * @param source     源参数
     * @param parameters 方法参数列表
     */
    private Integer getParamIndex(String source, String[] parameters) {
        for (int i = 0; i < parameters.length; i++) {
            if (source.equals(parameters[i]))
                return i;
        }
        return -1;
    }
}

搞定收工.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我之前给出的代码有误。确实,通过自定义注解AOP实现指定接口在没有Token的情况下可以访问的功能是不正确的。在Spring Security中,AOP是无法绕过安全过滤器链进行授权和认证的。 要实现指定接口在没有Token的情况下可以访问,可以考虑以下方法: 1. 创建一个自定义过滤器`NoTokenFilter`,用于拦截指定的接口,并跳过Spring Security的Token验证。 ```java @Component public class NoTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获取请求路径 String requestPath = request.getRequestURI(); // 判断是否为需要跳过Token验证的接口 if (isNoTokenRequired(requestPath)) { filterChain.doFilter(request, response); return; } // 非跳过Token验证的接口,执行Spring Security的Token验证逻辑 // ... } private boolean isNoTokenRequired(String requestPath) { // 判断请求路径是否为需要跳过Token验证的接口 // 返回true表示需要跳过Token验证,返回false表示不需要跳过Token验证 // ... } } ``` 2. 配置Spring Security,将自定义过滤器添加到Spring Security的过滤器链中。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private NoTokenFilter noTokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 配置需要Token验证的接口 .anyRequest().authenticated() .and() .csrf().disable(); // 将自定义过滤器添加到Spring Security的过滤器链中 http.addFilterBefore(noTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Bean public NoTokenFilter noTokenFilter() { return new NoTokenFilter(); } } ``` 3. 在需要不需要Token验证的接口上,使用`@NoTokenRequired`注解标记。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NoTokenRequired { } ``` ```java @RestController public class ExampleController { @NoTokenRequired @GetMapping("/example") public String example() { return "This API does not require Token"; } } ``` 通过以上配置,带有`@NoTokenRequired`注解的接口将会跳过Spring Security的Token验证,即可在没有Token的情况下访问该接口。其他接口仍然需要进行Token验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值