Quarkus基于注解的拦截器,带有非可选参数

huangapple go评论99阅读模式
英文:

Quarkus Annotation-Based Interceptor with Non-Optional Arguments

问题

这似乎是一个热门话题,基于提出的问题数量,但是我尚未找到我正在寻找的答案。我想在我的 Quarkus 应用中实现一个简单的授权服务,但我似乎一遍又一遍地重复着代码。

基本上,我从授权 HTTP 标头中获取 JWT,然后检查其中提供的角色是否足以访问我的终端点:

  1. public void someApiCall(@Context HttpHeaders headers) {
  2. authService.validate(ApiToken.SOME_API_CALL, headers); // 未经授权时抛出异常
  3. //…
  4. }

现在,我认为这看起来很笨拙,我不喜欢我需要为每个单独的 Http 终端点添加的额外参数。我已经研究了一些关于 AOP 的资料,并且知道如何添加一个拦截器,该拦截器可以通过一个注解验证 Http 标头,该注解将应用于我的方法:

  1. @Authorize
  2. public void someApiCall(/*…*/) { /*…*/ }

问题是,我不知道如何将参数传递到此注解中,以指定所需的角色。我想要类似这样的东西:

  1. @Authorize(UserRole.SYSADMIN)

这似乎非常简单,但我无法弄清楚。在下面,您将找到拦截器和注解类(当然缺少所需的角色):

Authorize.java

  1. @Retention(value=RUNTIME)
  2. @Target(value=METHOD)
  3. public @interface Authorize {}

AuthorizeInterceptor.java

  1. @Interceptor
  2. @Priority(3000)
  3. @Authorize
  4. public class AuthorizeInterceptor {
  5. @Inject
  6. AuthorizationService authService;
  7. @AroundInvoke
  8. public void validateRole(InvokationContext ctx) {
  9. authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
  10. }
  11. }

RestEndpoint.java

  1. public class RestEndpoint {
  2. @Context
  3. HttpHeaders headers;
  4. public HttpHeaders getHttpHeaders() { return headers; }
  5. }

SomeResource.java

  1. public class SomeResource extends RestEndpoint {
  2. @GET
  3. @Authorize
  4. public Object someApiCall() {
  5. /* 直接编写代码 */
  6. }
  7. }

因此,总之,在我写 @Authorize 的地方,我想要写成 @Authorize(UserRole.SOME_ROLE)。提前谢谢!

英文:

This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.

Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:

  1. public void someApiCall(@Context HttpHeaders headers) {
  2. authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
  3. //…
  4. }

Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:

  1. @Authorize
  2. public void someApiCall(/*…*/) { /*…*/ }

The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:

  1. @Authorize(UserRole.SYSADMIN)

This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):

Authorize.java

  1. @Retention(value=RUNTIME)
  2. @Target(value=METHOD)
  3. public @interface Authorize {}

AuthorizeInterceptor.java

  1. @Interceptor
  2. @Priority(3000)
  3. @Authorize
  4. public class AuthorizeInterceptor {
  5. @Inject
  6. AuthorizationService authService;
  7. @AroundInvoke
  8. public void validateRole(InvokationContext ctx) {
  9. authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
  10. }
  11. }

RestEndpoint.java

  1. public class RestEndpoint {
  2. @Context
  3. HttpHeaders headers;
  4. public HttpHeaders getHttpHeaders() { return headers; }
  5. }

SomeResource.java

  1. public class SomeResource extends RestEndpoint {
  2. @GET
  3. @Authorize
  4. public Object someApiCall() {
  5. /* do code directly */
  6. }
  7. }

So, in conclusion, where I write @Authorize, I want to have @Authorize(UserRole.SOME_ROLE).

Thanks in advance!

答案1

得分: 2

所以,我成功地找到了解决方法。原来并不是那么困难,我只是不知道该去哪里找。

以下是修改后的类:

Authorize.java

  1. @InterceptorBinding
  2. @Retention(RUNTIME)
  3. @Target({TYPE, METHOD})
  4. public @interface Authorize {
  5. // Nonbinding 非常重要。它使得拦截器会在不考虑值的情况下触发
  6. @Nonbinding ApiToken value();
  7. }

AuthorizeInterceptor.java

  1. @Interceptor
  2. @Priority(3000)
  3. @Authorize(ApiToken.NULL)
  4. public class AuthorizeInterceptor {
  5. /* 字段 */
  6. public Object validate(InvokationContext ctx) throws Exception {
  7. authService.validate(/* 保持不变 */);
  8. return ctx.proceed();
  9. }
  10. }

SomeResource.java

  1. public class SomeResource {
  2. @GET
  3. @Authorize(ApiToken.SOME_API_CALL)
  4. public Object someApiCall() { /* 实现 */ }
  5. }

正如Turing85所指出的,JavaEE中已经存在一个类似的API,它以相同的方式实现了授权功能。

英文:

So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.

Here are the modified classes:

Authorize.java

  1. @InterceptorBinding
  2. @Retention(RUNTIME)
  3. @Target({TYPE, METHOD})
  4. public @interface Authorize {
  5. // Nonbinding is very important. It makes the interceptor fire regardless of the value
  6. @Nonbinding ApiToken value();
  7. }

AuthorizeInterceptor.java

  1. @Interceptor
  2. @Priority(3000)
  3. @Authorize(ApiToken.NULL)
  4. public class AuthorizeInterceptor {
  5. /* fields */
  6. public Object validate(InvokationContext ctx) throws Exception {
  7. authService.validate(/* stays the same */);
  8. return ctx.proceed();
  9. }
  10. }

SomeResource.java

  1. public class SomeResource {
  2. @GET
  3. @Authorize(ApiToken.SOME_API_CALL)
  4. public Object someApiCall() { /* implementation */ }
  5. }

As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.

huangapple
  • 本文由 发表于 2020年8月14日 23:27:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63415713.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定