在我们的开发过程中,用户登录校验是一个非常常见且必要的功能。为了让这个过程更加高效和简洁,我们可以通过AOP(面向切面编程)来实现。在这篇博客中,我将为大家介绍一个自定义的登录校验切面,并带你一步步解读其中的实现逻辑。
1. 什么是AOP以及为什么使用AOP
AOP,即面向切面编程,是一种编程范式,旨在通过分离关注点来提高代码的模块化。在实际开发中,有很多横切关注点(如日志记录、权限验证、事务管理等)会在多个地方反复出现。使用AOP,可以将这些关注点从业务逻辑中分离出来,保持代码的清晰和可维护性。
2. 自定义登录校验切面的实现
下面是实现代码,核心是通过AOP切面在执行控制器方法之前进行用户登录状态的校验:
@Component
@Aspect
@RequiredArgsConstructor
public class GuiguLoginAspect {
private final RedisTemplate redisTemplate;
@Around("execution(* com.atguigu.daijia.*.controller.*.*(..)) &&@annotation(guiguLogin)")
public Object login(ProceedingJoinPoint proceedingJoinPoint, GuiguLogin guiguLogin) throws Throwable {
// 1.获取request对象
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) attributes;
HttpServletRequest request = sra.getRequest();
// 2.从请求头获取token
String token = request.getHeader("token");
// 3.判断token是否为空,如果为空,返回登录提示
if (!StringUtils.hasText(token)) {
throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
}
// 4.token不为空,查询redis
String customerId = (String) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
// 5.查询redis得到用户id,把用户id放到Threadlocal里面
if (StringUtils.hasText(customerId)) {
AuthContextHolder.setUserId(Long.valueOf(customerId));
}
// 6.执行业务方法
return proceedingJoinPoint.proceed();
}
}
3. 代码解读
-
获取
HttpServletRequest
对象
在切面中,我们首先需要获取当前的HttpServletRequest
对象,以便从请求头中提取token
。通过RequestContextHolder.getRequestAttributes()
方法,我们可以轻松获取到当前请求的上下文信息。 -
提取并验证Token
从请求头中提取出token
是下一步的关键操作。token
通常是用户身份验证的唯一标识。我们使用StringUtils.hasText(token)
方法来判断token
是否为空或无效。如果token
无效,我们会抛出自定义的GuiguException
异常,提示用户需要登录。 -
从Redis中查询用户信息
接下来,我们通过RedisTemplate
来查询Redis中是否存在对应的用户信息(通常是用户的ID)。如果找到了用户信息,我们将用户ID存储在ThreadLocal
中,以便后续业务逻辑能够访问。 -
执行业务方法
最后,如果用户通过了验证,切面将继续执行原本的方法,即继续执行业务逻辑。
4. 自定义注解GuiguLogin
为了让这个切面生效,我们需要在需要进行登录校验的方法上使用自定义注解@GuiguLogin
:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GuiguLogin {
// 自定义注解,无需额外内容
}
使用这个注解后,GuiguLoginAspect
切面会在方法执行前自动触发登录校验逻辑。
5. 总结
通过AOP,我们可以优雅地实现登录校验,将繁琐的验证逻辑从业务代码中抽离出来,既提高了代码的可读性,也增强了系统的模块化。在实际开发中,这种设计模式能帮助我们更好地管理和维护代码,尤其是在需要频繁进行类似验证的场景下。