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
我正在调查使用 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