暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

aop结合token做身份认证

长夜难眠 2020-07-09
319

点击蓝字

关注不迷途


背景


当系统有多个身份时,该如何快速有效地实现身份的认证?


案例分析


目前手上有个项目划分的身份有超级管理员、管理员、普通用户。


超级管理员和管理员是在同一个表内:admin。


普通用户在另一个表内:user。


当然,这不是重点,重点是要分析这三个身份的关系。


首先,后台管理系统只能是超级管理员和管理员使用,而管理员在功能上是没有超级管理员的多,管理员是超级管理员的弟第,也就是说超级管理员包含着管理员,这是一种包含关系。


普通用户只能在小程序端进行登录,那按这么分析,普通用户是不是独立存在的?


那当然不是,虽说后者和前两者在不同的端上,但本质上要限制不同身份的调用接口权限,而重点不是在前端上的不同。


而必不可少的是一个接口可以被后台管理系统和微信小程序同时调用,那作为管理端是不是应该拥有调用普通用户接口的权限呢?


是的,管理端是包含着普通用户权限的。


权限:超级管理员 > 管理员 > 普通用户


行动


我们都知道可以使用token来进行用户登录态的保存,也就是token的作用是能够判断一个用户是否已经登录,这个也是调用受限接口的前提条件。


那么再进一步,我们在token中存储我们的身份信息,那么是否可以即验证用户是否登录的同时也验证了用户的身份呢?


好的,接下来的实践检验真理的过程。


第一步,建模token-subject,也就是token存储的主题。


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Token {
private String identifier;//用户识别
private String role;//用户身份
}
复制



前面的token解析可以取得role,role是判断身份的重点。


重点是aop怎样写?


@Aspect
@Component
public class TokenValid {
private static final Logger log = LoggerFactory.getLogger(TokenValid.class);


/**
* 身份验证切点
*/
@Pointcut("execution (* com.bmt.web.*.*(..))&&" + "@annotation(com.bmt.annotation.SuperAdminValid)")
public void superAdminValid() {}


/**
* 身份验证切点
*/
@Pointcut("execution (* com.bmt.web.*.*(..))&&" + "@annotation(com.bmt.annotation.AdminValid)")
public void adminValid() {}


/**
* 身份验证切点
*/
@Pointcut("execution (* com.bmt.web.*.*(..))&&" + "@annotation(com.bmt.annotation.UserValid)")
public void userValid() {}


/**
* 超级管理员鉴权
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "superAdminValid()")
public Object verifySuperAdmin(ProceedingJoinPoint joinPoint) throws Throwable {


Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
String token = request.getHeader("token");
log.info("验证token:{}", token);
if (token == null) {
throw new ResultException(ResultEnum.UNAUTH);
}
Claims claims = TokenUtils.parseJWT(token);
ObjectMapper mapper = new ObjectMapper();


Token tokenObj = mapper.readValue(claims.getSubject(), Token.class);
log.info("token:{}", tokenObj);
if (tokenObj == null || !"超级管理员".equals(tokenObj.getRole())) {
throw new ResultException(ResultEnum.UNAUTH);
}
} catch (ResultException e) {
throw new ResultException(ResultEnum.UNAUTH);
} catch (ExpiredJwtException ej) {
throw new ResultException(ResultEnum.UNAUTH);
}




return joinPoint.proceed(args);
}


/**
* 管理员鉴权:超级管理员或者管理员
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "adminValid()")
public Object verifyAdmin(ProceedingJoinPoint joinPoint) throws Throwable {


Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
String token = request.getHeader("token");
log.info("验证token:{}", token);
if (token == null) {
throw new ResultException(ResultEnum.UNAUTH);
}
Claims claims = TokenUtils.parseJWT(token);
ObjectMapper mapper = new ObjectMapper();


Token tokenObj = mapper.readValue(claims.getSubject(), Token.class);
log.info("token:{}", tokenObj);
if (tokenObj == null || !("超级管理员".equals(tokenObj.getRole()) || "管理员".equals(tokenObj.getRole()))) {
throw new ResultException(ResultEnum.UNAUTH);
}
} catch (ResultException e) {
throw new ResultException(ResultEnum.UNAUTH);
} catch (ExpiredJwtException ej) {
throw new ResultException(ResultEnum.UNAUTH);
}




return joinPoint.proceed(args);
}


/**
* 用户鉴权:存在token就是了
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "userValid()")
public Object verifyUser(ProceedingJoinPoint joinPoint) throws Throwable {


Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
String token = request.getHeader("token");
log.info("验证token:{}", token);
if (token == null) {
throw new ResultException(ResultEnum.UNAUTH);
}
Claims claims = TokenUtils.parseJWT(token);
ObjectMapper mapper = new ObjectMapper();


Token tokenObj = mapper.readValue(claims.getSubject(), Token.class);
log.info("token:{}", tokenObj);
} catch (ResultException e) {
throw new ResultException(ResultEnum.UNAUTH);
} catch (ExpiredJwtException ej) {
throw new ResultException(ResultEnum.UNAUTH);
}




return joinPoint.proceed(args);
}
}
复制



aop的切点会定位到不同的Controller方法,同时使用注解去标注该方法是被切点定位的。


切点有三,superAdminValid,adminValid,userValid,在具体的Controler方法上使用注解@SuperAdminValid,@AdminValid,@UserValid可以使对应的切点生效。


相应的注解也很简单,不需要什么别的信息,只是标注方法的。


/**
* 验证Admin token的注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AdminValid {


}
复制



切点生效后该执行增强方法了,增强方法就是先获取token,再解析获取role,再对role进行判断。


SuperAdmin判断role是否为超级管理员。


Admin判断是否为超级管理员或者管理员。


User判断token是否存在即可。


总结


不同的需求可以使用已有知识进行延伸拓展,交织出解决方案。



喜欢本篇内容顺便点个在看吧


文章转载自长夜难眠,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论