Vert.x Web模块(三)

时间:2021-02-06 18:03:15


认证/授权

Vert.x有一些创新的处理器用于认证和授权。

 

创建一个认证处理器

为了创建一个认证处理器,需要一个AuthProvider的实例。认证提供者被用于用户的认证和授权。Vert.xvertx-aut项目中提供了多个认证提供者创新实例。关于认证提供者的及怎样使用和配置的完整信息,请咨询认证文档。

这是一个用认证提供者创建基本认证处理器的例子:

router.route().handler(CookieHandler.create());

router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

 

AuthHandler basicAuthHandler =BasicAuthHandler.create(authProvider);

 

在你的应用中处理认证

假设想给访问以/private/开头路径的请求被认证。为了做到这点,请确何你的认证处理器配置在这此路径的应用处理器这前: router.route().handler(CookieHandler.create());

router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

router.route().handler(UserSessionHandler.create(authProvider));

 

AuthHandler basicAuthHandler =BasicAuthHandler.create(authProvider);

 

// All requests to paths starting with '/private/' willbe protected

router.route("/private/*").handler(basicAuthHandler);

 

router.route("/someotherpath").handler(routingContext-> {

 

  // This will bepublic access - no login required

 

});

 

router.route("/private/somepath").handler(routingContext-> {

 

  // This willrequire a login

 

  // This will havethe value true

  booleanisAuthenticated = routingContext.user() != null;

 

});

如果认证处理器已经成功认证和授权用户,Vert.x会将User对象注入到RoutingContext,所以用user方法获取。如是想将User对象存入会话在请求间共享同是不必在每个请求进行认证。应确保在认证处理器之前,设置会话处理器和一个与路由匹配的用户会话处理器。一旦获取user对象,就在程序中调用授权用户的方法。如果用户登出,可以调用路由上下文中的clearUser方法。

 

HTTP基本认证

HTTP基本认证是一个简单的认证手段,适用于简单应用。用基本认证,凭证以未加密的形式用HTTP头在网上传输,所以在应用中使用HTTPS而不是HTTP显得很重要。使用基本认证,如果用户请求的资源需要授本,基本认证的处理器会发回一个WWW-Authenticate集合的401请求头。这就是提示浏览器显示一个登录窗口并提示用户输入用户名和密码。此时带着Authorization头,再次发对此资源的请求。Authorization头包含了用Base64编码的用户名和密码。在基础认证处理器收到此信息后,认证处理器将用用户名和密码调用配置好的AuthProvider给用户进行授权。如果成功,然后请求的路由允许继续调用应用处理器。相反一个403请求将返回给浏览器,说明访问被拒绝。认证处理器可以设置权限集合,用来给资源访问进行控制。

 

重定向认证处理器

如果在放问受保护的资源并没有登录,需重定向认证处理的用户被重定向到登录页面。然后用户填 写表单并提交。认证服务器对用户进行认证处理,是否将用户重定向回访问的原始资源。为了使用重定向认证,需要配置RedirectAuthHadler代替基础认证处理器。也需要设置服务于实际登录页的处理器,此处理器有于处理实际登录。为了处理登录,我们提供了预编译的FormLoginHandler处理器类。

这是一个简单的例子,在默认的重定向URL /loginpage,使用重定义认证处理器。

router.route().handler(CookieHandler.create());

router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

router.route().handler(UserSessionHandler.create(authProvider));

 

AuthHandler redirectAuthHandler =RedirectAuthHandler.create(authProvider);

 

// All requests to paths starting with '/private/' willbe protected

router.route("/private/*").handler(redirectAuthHandler);

 

// Handle the actual login

router.route("/login").handler(FormLoginHandler.create(authProvider));

 

// Set a static server to serve static resources, e.g.the login page

router.route().handler(StaticHandler.create());

 

router.route("/someotherpath").handler(routingContext-> {

  // This will bepublic access - no login required

});

 

router.route("/private/somepath").handler(routingContext-> {

 

  // This willrequire a login

 

  // This will havethe value true

  booleanisAuthenticated = routingContext.user() != null;

 

});

 

JWT授权

对于JWT授权的资源,可用权限和用户进行保护,没有足够的权限将会拒绝访问。为了使用此处理器需要两步:

  • 一个处理 器发布令牌(或者依赖第三方)

  • 一个过滤请求的处理器

这两个处理器仅能用于HTTPS,不使用HTTPS,令处牌可能在传输的过程中被控测导致会话被劫持。这是一个怎样发布令牌的例子:
 Router router = Router.router(vertx);

 

JsonObject authConfig = newJsonObject().put("keyStore", new JsonObject()

   .put("type", "jceks")

   .put("path", "keystore.jceks")

   .put("password", "secret"));

 

JWTAuth authProvider = JWTAuth.create(vertx, authConfig);

 

router.route("/login").handler(ctx -> {

  // this is anexample, authentication should be done with another provider...

  if("paulo".equals(ctx.request().getParam("username"))&& "secret".equals(ctx.request().getParam("password"))){

   ctx.response().end(authProvider.generateToken(newJsonObject().put("sub", "paulo"), new JWTOptions()));

  } else {

    ctx.fail(401);

  }

});

现在,客户端有了令牌,对于所有后续的所有请求需要用Bearer <token>值的AuthorizationHTTP头,例如:

Router router = Router.router(vertx);

 

JsonObject authConfig = newJsonObject().put("keyStore", new JsonObject()

   .put("type", "jceks")

   .put("path", "keystore.jceks")

   .put("password", "secret"));

 

JWTAuth authProvider = JWTAuth.create(vertx, authConfig);

 

router.route("/protected/*").handler(JWTAuthHandler.create(authProvider));

 

router.route("/protected/somepage").handler(ctx-> {

  // some handlecode...

});

JWT允许您将任何你喜欢的信息添加到令牌本身。这样做服务器上不会有状态,这就允行你在不需要集群会话数据是进行应用伸缩。为了向令牌添加数据,可以在令牌创建时向JsonObject参数添加数据:

JsonObject authConfig = newJsonObject().put("keyStore", new JsonObject()

   .put("type", "jceks")

   .put("path", "keystore.jceks")

   .put("password", "secret"));

 

JWTAuth authProvider = JWTAuth.create(vertx, authConfig);

 

authProvider.generateToken(newJsonObject().put("sub", "paulo").put("someKey","some value"), new JWTOptions());

 

然后要以这样使用数据:

Handler<RoutingContext> handler = rc -> {

  String theSubject= rc.user().principal().getString("sub");

  String someKey =rc.user().principal().getString("someKey");

};

 

配置需要的权限

对于认证处理器,也可以对访问的资源配置需要的要利。默认,如果没有配置权限,登录访问资源就足够了,然而用户必须登录(认证)并俱有访问权限。这是一个权限配置的例子,应用的不同部分需要不同的访问权限。注意授权的方法由后面的认证处理器进行决定,你可用一些支持角色/许可的基本模型,也可以用其他模型。

AuthHandler listProductsAuthHandler =RedirectAuthHandler.create(authProvider);

listProductsAuthHandler.addAuthority("list_products");

 

// Need "list_products" authority to listproducts

router.route("/listproducts/*").handler(listProductsAuthHandler);

 

AuthHandler settingsAuthHandler =RedirectAuthHandler.create(authProvider);

settingsAuthHandler.addAuthority("role:admin");

 

// Only "admin" has access to/private/settings

router.route("/private/settings/*").handler(settingsAuthHandler);