Vaadin身份验证和授权

5
我是Vaadin的新手,之前我做过一个JSF Web应用程序。我有一个ManagedBean来执行用户登录操作,使用安全域(security-realm)委托实际验证凭据。

在Vaadin中,我该如何做这个?有最佳实践吗? 我已经到了只想随便拼凑一下的地步,但肯定有某种标准流程,不是吗!? 我找到了一些教程,但大多数都使用Spring(我想使用EJB)。 此外,每个教程似乎都过于复杂了。

对于如此常见的内容,一定有一些简单而完整的教程。

3个回答

3
这是我能找到的关于如何使用JAAS保护Vaadin应用程序的唯一官方文章。我指的是本地或官方的Vaadin安全性。如果您使用Spring安全性、Shiro,您会发现更多内容,但纯ESB使用JAAS。 我同意这篇文章不足以学习如何设置应用程序。在此分享我的经验:
1. Vaadin CDI扩展:
<!-- Vaadin Official DI support. -->
<dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-cdi</artifactId>
  <version>1.0.0.alpha2</version>
</dependency>

2. 理解JAAS

我建议您阅读概述文章,了解JAAS在非Vaadin应用程序中的工作方式,因为JAAS取决于服务器应用程序提供者(TomEE、Jboss、Wildfly、Glasfish)都有不同的配置设置。 在这里,您将找到使用Tomee的概念验证

3. 开发自己的登录模块。

如果您已经决定创建自定义登录模块,例如:

public class MyLoginModule implements LoginModule {
private CallbackHandler handler;
private Subject subject;
private UserPrincipal userPrincipal;
private RolePrincipal rolePrincipal;
private String login;
private List<String> userGroups;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {
handler = callbackHandler;
this.subject = subject;
}
@Override
public boolean login() throws LoginException {
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("login");
callbacks[1] = new PasswordCallback("password", true);
try {
handler.handle(callbacks);
String name = ((NameCallback) callbacks[0]).getName();
String password = String.valueOf(((PasswordCallback) callbacks[1]).getPassword());
// Here we validate the credentials against some
// authentication/authorization provider.
// It can be a Database, an external LDAP, a Web Service, etc.
// For this tutorial we are just checking if user is "user123" and
// password is "pass123"
if (name != null && name.equals("admin") && password != null && password.equals("admin")) {
login = name;
userGroups = new ArrayList<String>();
userGroups.add("admin");
return true;
}
// If credentials are NOT OK we throw a LoginException
throw new LoginException("Authentication failed");
} catch (IOException e) {
throw new LoginException(e.getMessage());
} catch (UnsupportedCallbackException e) {
throw new LoginException(e.getMessage());
}
}
@Override
public boolean commit() throws LoginException {
userPrincipal = new UserPrincipal(login);
subject.getPrincipals().add(userPrincipal);
if (userGroups != null && userGroups.size() > 0) {
for (String groupName : userGroups) {
rolePrincipal = new RolePrincipal(groupName);
subject.getPrincipals().add(rolePrincipal);
}
}
return true;
}
@Override
public boolean abort() throws LoginException {
return false;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
subject.getPrincipals().remove(rolePrincipal);
return true;
}
}

在应用的META-INF/context.xml中引用它。这些设置适用于TomEE,但对于glashfish或其他应该类似。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Realm className="org.apache.catalina.realm.JAASRealm" appName="myrealm" userClassNames="net.sf.jaas.auth.UserPrincipal"
    roleClassNames="net.sf.jaas.auth.RolePrincipal" />
</Context>

请注意,userClassNames和roleClassNames只是简单的Java Pojo,但必须实现java.security.Principal;
3. 将JAAS集成到您的Vaadin登录视图中
使用Vaadin TextField和Password定义您喜欢的登录表单,并在doLoginEvent()中引用JAAS。每当调用JaasAccessControl.login时,都会创建您的LoginModule的新实例。
  import com.vaadin.cdi.access.JaasAccessControl;
  try {
            JaasAccessControl.login(loginEvent.getUsername(), loginEvent.getPassword());
            logger.info("User {} authenticated", getPrincipalName());
            navigator.navigateTo(Main.NAME);
        } catch (Exception e) {
            Notification.show("Error logging in", Type.ERROR_MESSAGE);
            logger.error(e.getMessage(), e);
        }

4. 使用已登录用户的信息。

无论何时用户通过JAAS登录过滤器,他的请求上下文中都将可用一个标识他的类。这个类被称为Principal(即您先前在LoginModule类中设置的类)。

public boolean isUserInRole(String role) {
        return JaasAccessControl.getCurrentRequest().isUserInRole(role);
    }

    public String getPrincipalName() {
        Principal principal = JaasAccessControl.getCurrentRequest().getUserPrincipal();
        if (principal != null) {
            return principal.getName();
        }

        return null;
    }

    public boolean isUserSignedIn() {
        Principal principal = JaasAccessControl.getCurrentRequest().getUserPrincipal();
        return principal != null;
    }

5. LoginModule的替代方案

创建自定义登录模块并非必须,通常Java EE提供者(如Tomee)会提供少量实现。可以基于属性文件或某些数据库表进行身份验证。 请参阅Tomee文档JAAS以查看一些示例:

6. 在TomEE中启用JAAS 您需要创建一个jaas.config文件,其中引用您的LoginModule,例如:

filename: jaas.config 
myrealm{
    net.sf.jaas.MyLoginModule required;
};

然后使用此参数启动应用服务器:

-Djava.security.auth.login.config=E:/apache-tomee-jaxrs-1.6.0.2/conf/jaas.config

如果您想查看这个概念的证明,请查看此示例,它使用Tomee、Vaadin 7和JAAS。

1

几个月前我也遇到了同样的问题。当时我无法弄清楚它是如何工作的,所以我查看了Spring插件,现在我正在使用Vaadin登录表单和Spring安全性。


非常感谢您的回答。不过我会保留这个问题,因为可能会有人提出纯Vaadin的解决方案。 - fancy

1

有一个完整的示例使用JAAS、Vaadin 8、Vaadin CDI插件和内置的LoginForm在这里可用,步骤如下:

  1. pom.xml中启用Vaadin CDI插件
  2. 使用@CDIUI注释启用Vaadin UI中的CDI,并配置Navigator
  3. 通过添加@RolesAllowed注释(或任何其他javax.annotation.security注释)启用CDI视图中的授权
  4. 实现登录视图,该视图派生自内置的LoginForm并使用JaasAccessControl来验证用户。

一旦您了解组件如何配合使用,它实际上是一种非常好的流畅体验。

Vaadin wiki中有一篇更长的文章,详细解释了如何使用带数据库支持的JAAS进行身份验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接