一、spring Aop身份验证
一般,如果用户没有登录的话,用户只可以查看商品,但是其他的,比如支付等是不能够进行操作的,这个时候,我们就需要用到用户拦截, 或者说身份验证了。
首先定义一个类AuthorizeAspect
,以@Aspect
注解。
然后把所有以Controller
声明为切点,但排除UserController
,因为这个Controller就是验证用户登录的Controller。
@Pointcut("execution(public * com.sihai.controller *.*(..))"
"&& !execution(public * com.sihai.controller.UserController.*(..))")
public void verify(){}
最后对这个切点做一些前置处理,因为用户登录后,按照我们之前写的逻辑,cookie和redis中应该含有用户的信息,所以现在查询这两个地方,来验证用户有没有登录。
代码语言:javascript复制 @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 AuthorizeException();
}
//去redis查询,这个下面的redis用到的是springboot的redis工具类
String tokenValue=redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX,cookie.getValue()));
if (StringUtils.isEmpty(tokenValue)){
log.warn("Redis中查不到token");
throw new AuthorizeException();
}
}
完整代码如下:
代码语言:javascript复制@Aspect
@Component
@Slf4j
public class AuthorizeAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Pointcut("execution(public * com.sihai.controller. *.*(..))"
"&& !execution(public * com.sihai.controller. UserController.*(..))")
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 AuthorizeException();
}
//去redis查询
String tokenValue=redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX,cookie.getValue()));
if (StringUtils.isEmpty(tokenValue)){
log.warn(" Redis中查不到token");
throw new AuthorizeException();
}
}
}
二、springboot统一异常处理
- 自定义异常类
从以上代码中可以看到,如果用户没有登陆,就会抛出一个 AuthorizeException
的异常,这是一个自定义的异常。这个异常很简单,只有一个简单的定义,为运行时异常
public class AuthorizeException extends RuntimeException {}
之后我们需要定义一个对这个异常的处理器 ExceptionHandler
,当扑获到这个异常,说明用户没有登陆,那就重新调到登陆界面(访问处理登陆的Controller)。
- 创建全局异常处理类:通过使用
@ControllerAdvice
定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler
用来定义函数针对的异常类型,最后将Exception对象和请求URL映射到error.html
中
@ControllerAdvice
public class ExceptionHandler {
@Autowired
private ProjectUrlConfig projectUrlConfig;
//拦截登录异常
@ExceptionHandler(value = AuthorizeException.class)
public ModelAndView handlerAuthorizeException(){
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
- 实现error.html页面展示:在templates目录下创建error.html,将请求的URL和Exception对象的message输出。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>统一异常处理</title>
</head>
<body>
<h1>Error Handler</h1>
<div th:text="${url}"></div>
<div th:text="${exception.message}"></div>
</body>
</html>
启动该应用,访问:http://localhost:8080/hello,可以看到如下错误提示页面。