两种Controller层接口鉴权方式

  |   评论   |   浏览

两种Controller层接口鉴权方式

​ 最近在做一个即时通讯服务时,要求对每个接口的入参进行鉴权处理,这里我整理出来了两种方式:1.基于注解和拦截器鉴权 2.基于注解和AOP鉴权

这里我在采用的是aop的方式,拦截器这里只完成了伪代码进行作为记录。

1.基于注解和拦截器鉴权

拦截器的方式主要需要解决requestBody重复获取的问题

1.首先我们需要定义一个鉴权标识的注解

/** * 鉴权标记注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Authorization { }

2.重写HttpServletRequestWrapper

通过自定义的HttpServletRequestWrapper 备份一下流的数据,自定义HttpServletRequestWrapper 调用父类request.getInputStream()读取全部数据出来保存在一个byte数组内,当再次获取流数据的时候,自定义的HttpServletRequestWrapper 就会用byte数组重新生成一个新的流。备份的流数据仍然保留在byte数组中。

public class RepeatableReadRequestWrapper extends HttpServletRequestWrapper { private final byte[] bytes; public RepeatableReadRequestWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException { super(request); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); bytes = request.getReader().readLine().getBytes(); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bais.read(); } @Override public int available() throws IOException { return bytes.length; } }; } }

3.定义一个拦截器

@Component public class RepeatSubmitInterceptor implements HandlerInterceptor { @Autowired RedisCache redisCache; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); if (method.isAnnotationPresent(Authorization.class)) { //请求参数字符串 String nowParams = ""; if (request instanceof RepeatableReadRequestWrapper) { try { nowParams = ((RepeatableReadRequestWrapper) request).getReader().readLine(); System.out.println("nowParams: " + nowParams); //TODO 进行鉴权处理 } catch (IOException e) { e.printStackTrace(); } } } } return true; } }

4.定义一个过滤器

public class RepeatableRequestFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/json")) { RepeatableReadRequestWrapper requestWrapper = new RepeatableReadRequestWrapper(request, (HttpServletResponse) servletResponse); filterChain.doFilter(requestWrapper,servletResponse); return; } filterChain.doFilter(servletRequest, servletResponse); } }

5.配置类

@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired RepeatSubmitInterceptor repeatSubmitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); } @Bean FilterRegistrationBean<RepeatableRequestFilter> repeatableRequestFilterFilterRegistrationBean() { FilterRegistrationBean<RepeatableRequestFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new RepeatableRequestFilter()); bean.addUrlPatterns("/*"); return bean; } }

6.测试类

@RestController public class HelloController { @Authorization @PostMapping("/test") public String authorization(@RequestBody User user) { System.out.println(user); return "succes"; } }

测试结果

image-20220629160316179

2.基于注解和AOP鉴权

1.定义一个鉴权标识注解

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Authorization { }

2.定义AOP类

@Aspect @Order(value = Integer.MAX_VALUE-1) @Component public class AuthorizationAspect { private static Logger log = LoggerFactory.getLogger(AuthorizationAspect.class); @Autowired private AuthConfigService authConfigService; @Before(value = "@annotation(com.llp.api.annotation.Authorization))") public void authorization(JoinPoint joinPoint) throws Exception { //获取请求参数 Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); //获取方法签名 MethodSignature methodSignature = (MethodSignature) signature; //获取Method对象 Method method = methodSignature.getMethod(); //判断方法是否被我们自定义的@Authorization注解修饰 if (method.isAnnotationPresent(Authorization.class)) { //遍历请求参数 for (Object arg : args) { //判断请求参数是否属于BaseConfigRequest类型 if (arg instanceof BaseConfigRequest) { //属于,则对请求参数进行强转 BaseConfigRequest baseConfigRequest = (BaseConfigRequest) arg; log.info("鉴权基类-baseConfigRequest:{}", ObjectUtils.getDisplayString(baseConfigRequest)); //获取鉴权id String appId = baseConfigRequest.getAppId(); //获取密文 String cipherText = baseConfigRequest.getCipherText(); //通过鉴权id获取鉴权配置对象 EduAuthConfig authConfig = authConfigService.getAuthConfig(appId); log.info("authConfig:{}", ObjectUtils.getDisplayString(authConfig)); //如果通过鉴权id没有查询到则抛出异常鉴权失败 Assert.notNull(authConfig, "未查询到appId对应的鉴权配置信息,鉴权失败"); //根据密文、私钥进行解密获取到鉴权key String appKey = RSAUtil.decrypt(cipherText, authConfig.getPrivateKey()); log.info("解密后得到appKey:{}", appKey); //如果鉴权key为空或者解密出来的鉴权key和配置的鉴权key不相同则抛出异常鉴权失败 if (appKey == null || !appKey.equals(authConfig.getAppKey())) { throw new BaseException("鉴权参数错误,鉴权失败"); } //鉴权通过结束循环 return; } } } } }

3.定义一个鉴权基类

@Data @ApiModel(value = "鉴权基类") public class BaseConfigRequest<T> implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "鉴权Id") @NotBlank(message = "鉴权id不能为空") private String appId; @ApiModelProperty(value = "密文") @NotBlank(message = "密文不能为空") private String cipherText; @ApiModelProperty(value = "其他任意数据",notes = "在做鉴权时使用@Valid注解开启校验") @Valid private T data; }

4.测试方法

@Authorization @ApiOperation(value = "接收消息") @RequestMapping(value = "/receiveMsg", method = RequestMethod.POST) public BaseResult<String> receiveMsg(@Validated @RequestBody BaseConfigRequest<MessageSendRequest> request) { return BaseResult.judgeOperate(messageService.receiveMsg(request)); }

标题:两种Controller层接口鉴权方式
作者:llp
地址:https://llinp.cn/articles/2022/06/29/1656515117539.html