拦截器实现权限控制器以及防止重复提交的幂等性校验

    技术2023-09-29  79

    权限控制拦截器:顾名思义是做权限管理,例如,一个系统实现了10个功能,前5个功能只有管理员用户可使用,而不允许普通用户使用,这时就需要做权限的判断。当然,做权限控制的方法有很多,比如常见的现成框架spring security,shiro等等,但是在这里暂时先介绍一下单纯的使用拦截器做权限判断。 上一篇写了登录拦截器登录拦截器,并比较详细的介绍了拦截器的写法,所以在这篇就不再赘述了。 在我的项目里用户权限的体现就是用户表中的一个用户类型字段,对应着实体类中的userType属性。上一篇文章里介绍了我把userId,userName和userType拼接在一起再加密成token存入请求头中,所以我想要使用userType便只需要解析这个token就可以了。当然了,每个项目的做法与需求不同,在这里你只需要保证在浏览器的这次请求里你能拿到你所要用来判断权限的字段就可以了或者在拦截器里去数据库查找也ok,至于什么方法,以项目中的实际情况为准。话不多说,直接上代码,下面就是我们自己写的自定义注解和权限拦截器

    /** * 权限控制的自定义注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Authorization {} /** * 权限拦截器 */ @Component public class AuthorityInterceptor implements HandlerInterceptor { private static final Logger logger = Logger.getLogger(LoginInterceptor.class.getName()); private static final String TOKEN = "token"; @Resource private TokenRedisManage manage; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(!(handler instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; //自定义注解,在所需权限判断的方法上加上注解,如果没有该注解,直接放行 Authorization authorization = handlerMethod.getMethod().getAnnotation(Authorization.class); if(authorization == null){ return true; } //只写了这么个简单的逻辑判断 String token = request.getHeader(TOKEN); String userType = manage.fromTokenGetUserType(token); if (StringUtils.isBlank(token) || StringUtils.isBlank(userType)) { logger.info("header中没有token或token错误"); reSetResponse(response); return false; } //在这里没有做太多其他的判断,其他逻辑可以按具体项目需求继续写 return "2".equals(userType); } /** * 登录拦截 重置响应数据 * @param response * @throws IOException */ public void reSetResponse(HttpServletResponse response) throws IOException { response.reset(); response.setContentType("application/json;charset=UTF-8"); PrintWriter pw = response.getWriter(); pw.write(JsonUtil.jsonObject(ErrorConstant.LOGIN_FIRST.getCode(), ErrorConstant.LOGIN_FIRST.getMsg())); pw.flush(); pw.close(); } }

    我写的逻辑比较简单,如果方法需要权限判断则在此方法加上自定义注解@Authorization,拦截器判断请求的方法带此注解并userType等于2时,允许方法请求,否则拦截,我写的这个就类似一个模板,按自己需要添加逻辑就可以了。当然,权限拦截器和上一篇的登录拦截器以及下面要说的幂等性校验都可以写在一个拦截器类里,但是为了代码清晰,还是建议分开写。如果需要这里没展示的其他代码,可以从公众号里问我要,比较多就不贴了。 最后在拦截器注册器里加入我们所写的权限拦截器即可使用。

    @Resource private LoginInterceptor loginInterceptor; @Resource private AuthorityInterceptor authorityInterceptor; //注册登录拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { //登录拦截 registry.addInterceptor(loginInterceptor).addPathPatterns("/**")//添加所需要拦截的接口,这里我们拦截所有,但是 .excludePathPatterns("/userLogin/**"); //不拦截登陆和退出请求 //权限拦截 registry.addInterceptor(authorityInterceptor).addPathPatterns("/**") .excludePathPatterns("/userLogin/**"); }

    幂等性检验:幂等性校验其实就是防止请求重复提交。例如,提交订单,点击购买等以及常见项目中一些特殊情况下的添加修改等操作,接下来说的是使用拦截器+Redis实现的幂等性校验。 首先,我们自定义一个注解@IdenpotentApi,在需要防止重复请求的方法上加上此注解,如果不用注解,也可在拦截器注册器里进行接口添加,直接来看拦截器的代码。

    @Component public class IdempotentInterceptor implements HandlerInterceptor { private static final Logger logger = Logger.getLogger(IdempotentInterceptor.class.getName()); private static final String TOKEN = "token"; @Resource private TokenRedisManage manage; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!(handler instanceof HandlerMethod)) { return true; } //幂等性校验 HandlerMethod handlerMethod = (HandlerMethod) handler; //自定义注解 IdempotentApi idempotentApi = handlerMethod.getMethod().getAnnotation(IdempotentApi.class); if (idempotentApi == null) { logger.info(handlerMethod.getMethod().getName() + "方法不需要幂等性校验"); return true; } logger.info(handlerMethod.getMethod().getName() + "方法需要幂等性校验"); String token = request.getHeader(TOKEN); if(StringUtils.isBlank(token)){ return false; } String idempotentToken = token + handlerMethod.getMethod().getName(); //先判断redis中是否存在 boolean existIdempotentToken = manage.checkIdempotentToken(idempotentToken); if(BooleanUtils.isTrue(existIdempotentToken)){ reSetResponse(response); return false; } return manage.saveIdempotentTk2Redis(idempotentToken); } }

    请求进入拦截器后,首先判断此请求是否有我们上面写的这个注解,如果没有则直接放行。否则,开始逻辑,首先从请求头里拿到token,将token+此方法的名称作为我们在redis中保存的key,拿到之后我们先去redis中检查是否存在此同名的key,如果存在说明该请求是重复请求,则拦截器在响应中set提示信息并返回false,给响应赋值的方法在上面权限拦截器里有体现。如果redis中不存在此key,则说明是首次请求,将key存入到redis同时设置一分钟的过期时间,再在拦截器中返回true即可。 关注微信公众号:非定义式程序员,我们一起解决项目中遇到的更多问题。

    Processed: 0.017, SQL: 9