利用spring AOP 实现统一校验

开发环境

  • JDK: 1.7
  • spring: 4.0.6
  • aspect: 1.7.4

应用背景

  在APP与后台通讯的过程中,我们一般都会有个authToken的字符串校验,判断那些请求是需要校验用户信息的,因为APP用户并不需要登录到我们的后台系统,所以一些基于session的权限控制(比如shiro)并不合适,所以导致我们又回到了解放前,很多请求都需要先校验这个用户的信息,不通过的就重定向到登录界面,比如下面的代码:

    AppUser user = appUserService.getUserByAuthToken(appointmentSearchVo.getAuthToken());
    if (null == user) {
    throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
    }

  可以看到在很多的控制器下都会有这样一段代码,太丑陋了,像这些重复的但又必不可少的操作,用AOP的方式做统一处理是很优雅的,我的思路是:

  • 1.定义一个查询父类,里面包含到authToken跟usedId两个属性,所有需要校验用户的请求的查询参数都继承这个查询父类,之所以会有这个userId,是因为我们校验得到用户之后,需要根据用户Id获取一些用户数据的,所以在AOP层我们就回填了这个参数了,这样也不会影响之前的代码逻辑(这个可能跟我的业务需求有关了)
public class AuthSearchVO {
    
    public String authToken; //校验字符串
    
    public Integer userId; //APP用户Id
    
    public final String getAuthToken() {
        return authToken;
    }

    public final void setAuthToken(String authToken) {
        this.authToken = authToken;
    }

    public final Integer getUserId() {
        return userId;
    }

    public final void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Override
    public String toString() {
        return "SearchVO [authToken=" + authToken + ", userId=" + userId + "]";
    }

}
  • 2.定义一个方法级的注解,所有需要校验的请求都加上这个注解,用于AOP的拦截(当然你也可以拦截所有控制器的请求)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
    String type();
}
  • 3.AOP处理,之所以会将注解作为参数传进来,是因为考虑到可能会有多个APP的校验,可以利用注解的type属性加以区分
public class AuthTokenAOPInterceptor {
    
    @Resource
    private AppUserService appUserService;
    
    private static final String authFieldName = "authToken";
    private static final String userIdFieldName = "userId";
    
    public void before(JoinPoint joinPoint, AuthToken authToken) throws Throwable{
        
        Object[] args =  joinPoint.getArgs(); //获取拦截方法的参数
        boolean isFound = false;
        for(Object arg : args){
            if(arg != null){
                Class<?> clazz = arg.getClass();//利用反射获取属性值
                Field[]  fields =  clazz.getDeclaredFields();
                int authIndex = -1;
                int userIdIndex = -1;
                for(int i = 0; i < fields.length; i++){
                    Field field = fields[i];
                    field.setAccessible(true);
                    if(authFieldName.equals(field.getName())){//包含校验Token
                        authIndex = i;
                    }else if(userIdFieldName.equals(field.getName())){//包含用户Id
                        userIdIndex = i;
                    }
                }
                
                if(authIndex >= 0 & userIdIndex >= 0){
                    isFound = true;
                    authTokenCheck(fields[authIndex], fields[userIdIndex], arg, authToken);//校验用户
                    break;
                }
            }
        }
        if(!isFound){
            throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
        }
        
    }
    
    private void  authTokenCheck(Field authField, Field userIdField, Object arg, AuthToken authToken) throws Exception{
        if(String.class == authField.getType()){
            String authTokenStr = (String)authField.get(arg);//获取到校验Token
            AppUser user = appUserService.getUserByAuthToken(authTokenStr);
            if(user != null){
                userIdField.set(arg, user.getId());
            }else{
                throw new BizException(ErrorMessage.CHECK_AUTHTOKEN_FAIL);
            }
        }
        
    }
}
  • 4.最后就是在配置文件中配置这个AOP了(因为我们的spring版本跟aspect版本有点出入,导致用不了基于注解的方式)
    <bean id="authTokenAOPInterceptor" class="com.distinct.app.web.common.auth.AuthTokenAOPInterceptor"/>
    <aop:config proxy-target-class="true">
        <aop:pointcut id="authCheckPointcut" expression="@annotation(authToken)"/>
        <aop:aspect ref="authTokenAOPInterceptor" order="1">
            <aop:before method="before" pointcut-ref="authCheckPointcut"/>
        </aop:aspect>
    </aop:config>

  最后给出测试代码,这样的代码就优雅很多了

    @RequestMapping(value = "/appointments", method = { RequestMethod.GET })
    @ResponseBody
    @AuthToken(type="disticntApp")
    public List<AppointmentVo> getAppointments(AppointmentSearchVo appointmentSearchVo) {
        List<AppointmentVo> appointments = appointmentService.getAppointment(appointmentSearchVo.getUserId(), appointmentSearchVo);
        return appointments;
    }

转载于:https://www.cnblogs.com/coderhuang/p/5994055.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值