Wildfly 8 自定义登录模块访问 HttpServletRequest 参数

Wildfly 8 custom login module accessing HttpServletRequest params

我正在调查使用 wildfly 自定义登录模块的可行性。

客户端会将移动设备 ID 作为登录的一部分传递给服务器。我将以通常的方式检查用户名和密码是否正确,然后我需要检查移动设备是否已获准使用该服务。

我的想法是,我将有一个调用 HttpServletRequest.login(u, p) 的 restful 网络服务方法登录。

如何在 HttpServletRequest 的登录模块中获取移动设备 ID?

我可以登录,如果成功,则在网络服务中测试设备 ID,如果未获批准,则将用户注销。但这看起来有点乱。

正确的做法是什么?

编辑

反馈: 我按照克里斯建议的方式做了。我实现了自己的 CallBackHandler 版本和回调接口的实现,在我的登录模块的登录方法中,我执行以下操作:

public boolean login() throws LoginException {

        boolean login = super.login();

        if (login) {

            UuidCallback uuidCallback = new UuidCallback();

            try {

                super.callbackHandler.handle(new Callback[]{uuidCallback});

            } catch (Exception e) {

                LoginException le = new LoginException("Failed to get uuid");
                le.initCause(e);

                throw le;
            }

            System.out.print("Device UUID: "+uuidCallback.getUuid());
    }

    return login;
}

Web 服务内部登录方法:

@Path("/login")
@Produces({ "application/json" })
public class LoginWebService {

    @POST
    public Response login(@Context HttpServletRequest request) throws LoginException {

        CallbackHandler callbackHandler = new MyCallbackHandler(request.getParameter("username"), request.getParameter("password"), request.getParameter("uuid"));

        Subject subject = new Subject();
        LoginContext loginContext = new LoginContext("securityDomain", new subject, callbackHandler);

        loginContext.login();

        MyPrincipal principal = subject.getPrincipals(MyPrincipal.class).iterator().next();
    }
}

您也可以只在回调处理程序上设置 uuid,然后在 LoginModule.login 方法内的回调处理程序上调用 getUUID()。但我选择了这个设计,尽管它对我来说不太有意义。

登录并尝试访问受保护资源时我仍然收到 403 结果表明,如果 auth-constraint/role-name 是 *,您必须至少提供一个 security-role.

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>mydomain</realm-name>
</login-config>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>rest</web-resource-name>
        <url-pattern>/rest/app/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
</security-constraint>
<!-- after login there is a 403 on protected resources if no role and role-name * -->
<security-role>
    <role-name>user</role-name>
</security-role>

我的所有用户都有一个角色用户,这给了他们访问权限。我能够通过排除 security-role 来让它工作,但是 auth-constraint/role-name 必须设置为文字角色,在我的例子中:"user"

您可以在 Wildfly 中配置一个安全域,而不是直接从您的 restful 网络服务调用 HttpServletRequest.login() 方法。

这个安全域应该在你的 webapp web.xml 文件 login-config 元素中引用,带有安全约束,以启用 JAAS

这是一个 web.xml file 的示例,它声明了安全约束和登录配置(使用 BASIC 身份验证方法)

还有一个用于 security-domain 配置的 Wildfly standalone.xml 配置示例,使用标准提供的数据库登录模块,不是自定义的:

<security-domain name="securityDomain" cache-type="default">
    <authentication>
        <login-module code="Database" flag="required">
            <module-option name="dsJndiName" value="java:/TestDS"/>
            <module-option name="principalsQuery" value="select password from User where login = ? and (disabled is null or disabled = 0) and activated = 1"/>
            <module-option name="rolesQuery" value="select name,'Roles' from Role r, User_Role ur, User u where u.login=? and u.id = ur.userId and r.id = ur.roleId"/>
            <module-option name="hashAlgorithm" value="SHA-256"/>
            <module-option name="hashEncoding" value="base64"/>
            <module-option name="unauthenticatedIdentity" value="guest"/>
        </login-module>
    </authentication>
</security-domain>

然后,在您的 REST 资源中,您只需使用 @RolesAllowed 或 @PermitAll 注释来限制访问或不进行授权。

我建议创建一个 LoginContext 并通过一个 CallbackHandler 的实现。在回调处理程序中满足额外的 UUID 属性.

虽然此代码适用于我,但您需要对其进行更新以适应额外的 UUID 属性

public class NamePasswordCallbackHandler implements CallbackHandler {
    private final String username;
    private final String password;

    private NamePasswordCallbackHandler(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (Callback current : callbacks) {
            if (current instanceof NameCallback) {
                ((NameCallback) current).setName(username);
            } else if (current instanceof PasswordCallback) {
                ((PasswordCallback) current).setPassword(password.toCharArray());
            } else {
                throw new UnsupportedCallbackException(current);
            }
        }
    }
}

我创建了自己的配置对象实现(如下所示为 JBossJaasConfiguration)。

然后将此回调处理程序传递给您的 LoginContext:

CallbackHandler cbh = new NamePasswordCallbackHandler(username, password);
Configuration config = new JBossJaasConfiguration("mysqldomain");

LoginContext loginContext = new LoginContext("mycontext", new Subject(), cbh, config);
loginContext.login();

属性 "mysqldomain" 与您的 standalone.xml

中的安全域名有关
<subsystem xmlns="urn:jboss:domain:security:1.2">
    <security-domains>
        <security-domain name="mysqldomain" cache-type="default">
             <authentication>
                  <login-module code="com.soccerx.security.DatabaseServerLoginRealm" flag="required">
                     <module-option name="dsJndiName" value="java:jboss/datasources/SoccerSoftwareDS"/>
                     <module-option name="principalsQuery" value="select userId, tenantId, password, salt from User where username=? and StatusId != 2"/>

            <module-option name="rolesQuery" value="select Role, 'Roles' from User where Username=?"/>
                    <module-option name="password-stacking" value="useFirstPass"/>
                    <module-option name="principalClass" value="com.soccerx.security.DatabasePrincipal"/>
                </login-module>
            </authentication>
        </security-domain>
    <security-domains>
</subsystem>

虽然这不是满足您需求的完整解决方案,但我希望您可以修改它以确保在 UUID 不匹配时登录失败。

注意:您需要在 standalone.xml 中定义的 CustomDatabaseLoginRealm 中满足此要求。意思是访问 username/password/uuid 并将其与数据库中的值进行比较。

更多文档参见here