Skip to content

Spring AOP实现身份验证

sqmax edited this page Jan 1, 2019 · 3 revisions

之前在用户未登录的情况下,仍然可以访问我们的商品管理系统的资源,现在我们用Spring AOP来实现对访问的一些前置处理。

首先定义一个类SellerAuthorizeAspect,以@Aspect注解。

然后把所有以Seller开头的Controller声明为切点,但排除SellerUserController,因为这个Controller就是验证用户登录的Controller。

    @Pointcut("execution(public * com.imooc.controller.Seller*.*(..))"+
    "&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
    public void verify(){}

最后对这个切点做一些前置处理,因为用户登录后,按照我们之前写的逻辑,cookie和redis中应该含有用户的信息,所以现在查询这两个地方,来验证用户有没有登录。

   @Before("verify()")
    public void doVerify(){
        ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request=attributes.getRequest();

        //查询cookie
        Cookie cookie= CookieUtil.get(request,CookieConstant.TOKEN);
        if (cookie==null){
            log.warn("【登录校验】Cookie中查不到token");
            throw new SellerAuthorizeException();
        }

        //去redis查询
        String tokenValue=redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX,cookie.getValue()));
        if (StringUtils.isEmpty(tokenValue)){
            log.warn("【登录校验】 Redis中查不到token");
            throw new SellerAuthorizeException();
        }
    }

该类的完整代码如下:

@Aspect
@Component
@Slf4j
public class SellerAuthorizeAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Pointcut("execution(public * com.imooc.controller.Seller*.*(..))"+
    "&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
    public void verify(){}

    @Before("verify()")
    public void doVerify(){
        ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request=attributes.getRequest();

        //查询cookie
        Cookie cookie= CookieUtil.get(request,CookieConstant.TOKEN);
        if (cookie==null){
            log.warn("【登录校验】Cookie中查不到token");
            throw new SellerAuthorizeException();
        }

        //去redis查询
        String tokenValue=redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX,cookie.getValue()));
        if (StringUtils.isEmpty(tokenValue)){
            log.warn("【登录校验】 Redis中查不到token");
            throw new SellerAuthorizeException();
        }
    }

}

从以上代码中可以看到,如果用户没有登陆,就会抛出一个SellerAuthorizeException的异常,这是一个自定义的异常。这个异常很简单,只有一个简单的定义,为运行时异常

public class SellerAuthorizeException extends RuntimeException {}

之后我们需要定义一个对这个异常的处理器SellerExceptionHandler,当捕获到这个异常,说明用户没有登陆,那就重新跳到登陆界面。

@ControllerAdvice
public class SellerExceptionHandler {

    @Autowired
    private ProjectUrlConfig projectUrlConfig;

    //拦截登录异常
    //http://sqmax.natapp1.cc/sell/wechat/qrAuthorize?returnUrl=http://sqmax.natapp1.cc/sell/seller/login
    @ExceptionHandler(value = SellerAuthorizeException.class)
    public ModelAndView handlerAuthorizeException(){
        return new ModelAndView("redirect:"
        .concat(projectUrlConfig.getWechatOpenAuthorize())
        .concat("/sell/wechat/qrAuthorize")
        .concat("?returnUrl=")
        .concat(projectUrlConfig.getSell())
        .concat("/sell/seller/login"));
    }
}