四、权限规范
1、拦截器+MVC实现
概述:通过拦截去设置用户是否能够访问该接口
AdminAccessRightsInterceptor:该拦截器只允许管理员及以上身份才能访问,
TokenAuthenticationInterceptor:这两个拦截器都是用来进行token校验的,判断是否是系统用户,是否能访问接口。
通过token去获取用户信息,获取成功则说明token校验通过,可进行接口访问
public class TokenAuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object object) throws Exception {
String token = request.getHeader("Authorization");
Object obj = checkToken(token);
if(obj.getError()==0&&StringUtils.isNotBlank(obj.getData().get("UserID"))) {
request.setAttribute("acc_schoolid", obj.getData().get("SchoolID"));
request.setAttribute("acc_userid", obj.getData().get("UserID"));
try {
request.setAttribute("acc_username", URLDecoder.decode(obj.getData().get("UserName"),"UTF-8"));
} catch (Exception e) {
log.error("token认证成功,转码用户名称出现异常:{}",e);
request.setAttribute("acc_username", obj.getData().get("UserID"));
}
request.setAttribute("acc_usertype", obj.getData().get("UserType"));
request.setAttribute("acc_userclass", obj.getData().get("UserClass"));
request.setAttribute("acc_studylevel", obj.getData().get("StudyLevel"));
request.setAttribute("acc_photoPath", URLDecoder.decode(obj.getData().get("PhotoPath"),"UTF-8"));
return true;
}else {
returnErrorResponse(new PacketHttpRes_V32<>(ReturnCode.token_false_code,ReturnCode.token_false_message,ReturnCode.err_nomean_code,ReturnCode.err_nomean_message,null),response);
log.error("token认证失败,未认证的请求。error={},用户ID为{}",obj.getError(),obj.getData().get("UserID"));
return false;
}
}
}
将获取到的用户信息进行存储到request中,当需要用户信息的时候则从中拿出来使用
registry.addInterceptor(tokenAuthenticationInterceptor)
.addPathPatterns("/TeachingPlan/**")
.excludePathPatterns("/TeachingPlan/AddApplyInfo")
当我们写一些接口的时候,需时刻注意其权限规范,要注意其是否在拦截器的链路范围内,是否符合权限要求。
2、注解+切面实现
使用拦截器做安全权限校验
优点:可对于相同前缀的访问路径做同样的权限处理
registry.addInterceptor(tokenAuthenticationInterceptor)
.addPathPatterns("/TeachingPlan/**")
.excludePathPatterns("/TeachingPlan/AddApplyInfo")
缺点:如果用户拥有多角色多权限,那就得编写多个拦截器,配置多次该路径,最主要的是权限的区分不够细致,如果配置细致针对到每个接口,那么就会显得代码冗余杂乱,也失去了它自身的优点。
优化方案:
概述:编写UserAuthorized注释和切面实现类,通过对于添加了该注释的接口方法,当用户访问时进行切面拦截处理,进行前置的token校验与身份权限校验。
UserAuthorized注释:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAuthorized {
//是否起作用
boolean required() default true;
//需要拥有身份权限才能访问
String[] params() default {};
}
UserAuthorizeAspect切面实现类:
@Slf4j
@Aspect
@Component
public class UserAuthorizeAspect {
@Autowired
private RestTemplate restTemplate;
@Autowired
private GetAddressBySysId getAddressBySysId;
@Autowired
private BaseRequest baseRequest;
@Pointcut("@annotation(com.lancoo.common.security.annotation.UserAuthorized)")
private void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
UserAuthorized authorized = method.getAnnotation(UserAuthorized.class);
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = getToken(request);
Object obj = getUser(token);
checkLoginResult(request, obj);
checkUserPower(authorized, request);
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
/**
* 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
*
* @param joinPoint
*/
@AfterReturning("pointcut()")
public void doAfterReturning(JoinPoint joinPoint) {
}
}