认证/授权
Vert.x有一些创新的处理器用于认证和授权。
创建一个认证处理器
为了创建一个认证处理器,需要一个AuthProvider的实例。认证提供者被用于用户的认证和授权。Vert.x在vertx-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>值的Authorization的HTTP头,例如:
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);