雅加达EE 8安全> Wildfly 26 Elytron-未设定角色
我正在尝试使用Jakarta EE 8 Security设置一个简单的JSF登录,我已将登录页面作为自定义表单实现,如下所示:
@ApplicationScoped
@CustomFormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/user-login.xhtml",
useForwardToLogin = false,
errorPage = ""
)
)
@FacesConfig(version = FacesConfig.Version.JSF_2_3)
public class UserLoginConfig{
}
用户区域可确保以下servlet并定义单个角色“用户”:
@WebServlet("/user/*")
@DeclareRoles({"user"})
@ServletSecurity(@HttpConstraint(rolesAllowed = "user"))
public class UserLoginServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
}
简单的JSF表单提交到登录bean
@Named
@ViewScoped
public class LoginBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String password;
private String email;
@Inject private SecurityContext securityContext;
@Inject private Logger log;
public void login() throws IOException {
ExternalContext externalContext = Faces.getExternalContext();
AuthenticationStatus status = securityContext.authenticate(
(HttpServletRequest) externalContext.getRequest(),
(HttpServletResponse) externalContext.getResponse(),
AuthenticationParameters.withParams()
.credential(new UsernamePasswordCredential(email, password))
);
switch (status) {
case SEND_CONTINUE:
Faces.getContext().responseComplete();
break;
case SEND_FAILURE:
Faces.getContext().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login failed", null));
break;
case SUCCESS:
Faces.getContext().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "Login succeed", null));
externalContext.redirect(externalContext.getRequestContextPath() + "/user/home.xhtml");
break;
case NOT_DONE:
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
和背景bean触发了在自定义itentityStore
中实现的身份验证方法
@ApplicationScoped
public class MyIdentityStore implements IdentityStore {
@Inject private UserDAOQueries userDAOQueries;
@Inject private PasswordEncryptorEntities passwordEncryptor;
@Override
public int priority() {
return 90;
}
@Override
public Set<ValidationType> validationTypes() {
return EnumSet.of(ValidationType.VALIDATE);
}
@Override
public CredentialValidationResult validate(Credential credential) {
UsernamePasswordCredential login = (UsernamePasswordCredential) credential;
User user = userDAOQueries.findNonDeletedByEmail(login.getCaller());
if (user!=null) {
if(passwordEncryptor.isPasswordCorrect(login.getPasswordAsString(), user.getPassword())){
Set<String> roles = new HashSet<String>();
roles.add("user");
return new CredentialValidationResult(login.getCaller(), roles);
}else {
return CredentialValidationResult.INVALID_RESULT;
}
} else {
return CredentialValidationResult.NOT_VALIDATED_RESULT;
}
}
@Override
public Set<String> getCallerGroups(CredentialValidationResult validationResult) {
Set<String> roles = new HashSet<String>();
if(validationResult.getStatus().equals(Status.VALID)) {
roles.add("user");
}
return roles;
}
}
这一切都可以,并且用户被转发到/user/home.xhtml页面从服务器获得403禁止响应。 查看org.wildfly.security
跟踪我可以看到“用户”角色在身份验证后未传递给容器(或者未正确映射)。
11:47:16,055 TRACE [org.wildfly.security] (default task-4) Handling CallerPrincipalCallback
11:47:16,055 TRACE [org.wildfly.security] (default task-4) Original Principal = 'javax.security.enterprise.CallerPrincipal@317242d5', Caller Name = 'null', Resulting Principal = 'javax.security.enterprise.CallerPrincipal@317242d5'
11:47:16,056 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,057 INFO [io.undertow.accesslog] (default task-4) [14/Apr/2022:11:47:16 +0100] "POST /user-login.xhtml HTTP/1.1" 302 - - https HTTP/1.1
11:47:16,057 TRACE [org.wildfly.security.http.servlet] (default task-4) ServerAuthContext.validateRequest returned AuthStatus=AuthStatus.SEND_CONTINUE
11:47:16,065 TRACE [org.wildfly.security.http.servlet] (default task-4) Created ServletSecurityContextImpl enableJapi=true, integratedJaspi=false, applicationContext=my-webapp
11:47:16,065 TRACE [org.wildfly.security] (default task-4) Handling CallerPrincipalCallback
11:47:16,065 TRACE [org.wildfly.security] (default task-4) Original Principal = 'javax.security.enterprise.CallerPrincipal@317242d5', Caller Name = 'null', Resulting Principal = 'javax.security.enterprise.CallerPrincipal@317242d5'
11:47:16,066 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,066 TRACE [org.wildfly.security.http.servlet] (default task-4) ServerAuthContext.validateRequest returned AuthStatus=AuthStatus.SUCCESS
11:47:16,066 TRACE [org.wildfly.security] (default task-4) No roles request of CallbackHandler.
11:47:16,066 TRACE [org.wildfly.security.http.servlet] (default task-4) Storing SecurityIdentity in HttpSession
11:47:16,066 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,068 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,068 TRACE [org.wildfly.security] (default task-4) Permission mapping: identity [javax.security.enterprise.CallerPrincipal@317242d5] with roles [] implies ("javax.security.jacc.WebResourcePermission" "/user/home.xhtml" "GET") = false
11:47:16,069 INFO [io.undertow.accesslog] (default task-4) [14/Apr/2022:11:47:16 +0100] "GET /user/home.xhtml HTTP/1.1" 403 68 - https HTTP/1.1
我怀疑我在Elytron子系统中缺少一些配置代码>
在protionsow config I中,有一个应用程序 - 安全域定义为:
<application-security-domain name="other" security-domain="ApplicationDomain" enable-jaspi="true" integrated-jaspi="false"/>
在Elytron中,我的应用程序域配置为如下:
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
<realm name="local"/>
</security-domain>
使用默认的permission-mapper如下:
<simple-permission-mapper name="default-permission-mapper">
<permission-mapping>
<role name="user"/>
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
</permission-mapping>
</simple-permission-mapper>
我还尝试使用from from of proles-属性
角色解码器,但这似乎根本无法正常工作
,我只是试图告诉Wildfly用户已登录并具有“ x”角色分配...
I am trying to setup a simple JSF login using Jakarta EE 8 Security, I have implemented the login page as a custom form as follows:
@ApplicationScoped
@CustomFormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/user-login.xhtml",
useForwardToLogin = false,
errorPage = ""
)
)
@FacesConfig(version = FacesConfig.Version.JSF_2_3)
public class UserLoginConfig{
}
The user area is secured with following servlet and defines a single role 'user':
@WebServlet("/user/*")
@DeclareRoles({"user"})
@ServletSecurity(@HttpConstraint(rolesAllowed = "user"))
public class UserLoginServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
}
The simple JSF form submits to the LoginBacking bean
@Named
@ViewScoped
public class LoginBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String password;
private String email;
@Inject private SecurityContext securityContext;
@Inject private Logger log;
public void login() throws IOException {
ExternalContext externalContext = Faces.getExternalContext();
AuthenticationStatus status = securityContext.authenticate(
(HttpServletRequest) externalContext.getRequest(),
(HttpServletResponse) externalContext.getResponse(),
AuthenticationParameters.withParams()
.credential(new UsernamePasswordCredential(email, password))
);
switch (status) {
case SEND_CONTINUE:
Faces.getContext().responseComplete();
break;
case SEND_FAILURE:
Faces.getContext().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login failed", null));
break;
case SUCCESS:
Faces.getContext().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "Login succeed", null));
externalContext.redirect(externalContext.getRequestContextPath() + "/user/home.xhtml");
break;
case NOT_DONE:
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
And the backing bean triggers the authenticate method that is implemented within a custom ItentityStore
@ApplicationScoped
public class MyIdentityStore implements IdentityStore {
@Inject private UserDAOQueries userDAOQueries;
@Inject private PasswordEncryptorEntities passwordEncryptor;
@Override
public int priority() {
return 90;
}
@Override
public Set<ValidationType> validationTypes() {
return EnumSet.of(ValidationType.VALIDATE);
}
@Override
public CredentialValidationResult validate(Credential credential) {
UsernamePasswordCredential login = (UsernamePasswordCredential) credential;
User user = userDAOQueries.findNonDeletedByEmail(login.getCaller());
if (user!=null) {
if(passwordEncryptor.isPasswordCorrect(login.getPasswordAsString(), user.getPassword())){
Set<String> roles = new HashSet<String>();
roles.add("user");
return new CredentialValidationResult(login.getCaller(), roles);
}else {
return CredentialValidationResult.INVALID_RESULT;
}
} else {
return CredentialValidationResult.NOT_VALIDATED_RESULT;
}
}
@Override
public Set<String> getCallerGroups(CredentialValidationResult validationResult) {
Set<String> roles = new HashSet<String>();
if(validationResult.getStatus().equals(Status.VALID)) {
roles.add("user");
}
return roles;
}
}
This all works ok and the user is forwarded to the /user/home.xhtml page successfully, however I am then getting a 403 Forbidden response from the server.
Looking at the org.wildfly.security
TRACE I can see that the 'user' role is not being passed to the container following authentication (or they aren't being mapped correctly).
11:47:16,055 TRACE [org.wildfly.security] (default task-4) Handling CallerPrincipalCallback
11:47:16,055 TRACE [org.wildfly.security] (default task-4) Original Principal = 'javax.security.enterprise.CallerPrincipal@317242d5', Caller Name = 'null', Resulting Principal = 'javax.security.enterprise.CallerPrincipal@317242d5'
11:47:16,056 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,057 INFO [io.undertow.accesslog] (default task-4) [14/Apr/2022:11:47:16 +0100] "POST /user-login.xhtml HTTP/1.1" 302 - - https HTTP/1.1
11:47:16,057 TRACE [org.wildfly.security.http.servlet] (default task-4) ServerAuthContext.validateRequest returned AuthStatus=AuthStatus.SEND_CONTINUE
11:47:16,065 TRACE [org.wildfly.security.http.servlet] (default task-4) Created ServletSecurityContextImpl enableJapi=true, integratedJaspi=false, applicationContext=my-webapp
11:47:16,065 TRACE [org.wildfly.security] (default task-4) Handling CallerPrincipalCallback
11:47:16,065 TRACE [org.wildfly.security] (default task-4) Original Principal = 'javax.security.enterprise.CallerPrincipal@317242d5', Caller Name = 'null', Resulting Principal = 'javax.security.enterprise.CallerPrincipal@317242d5'
11:47:16,066 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,066 TRACE [org.wildfly.security.http.servlet] (default task-4) ServerAuthContext.validateRequest returned AuthStatus=AuthStatus.SUCCESS
11:47:16,066 TRACE [org.wildfly.security] (default task-4) No roles request of CallbackHandler.
11:47:16,066 TRACE [org.wildfly.security.http.servlet] (default task-4) Storing SecurityIdentity in HttpSession
11:47:16,066 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,068 TRACE [org.wildfly.security] (default task-4) Role mapping: principal [javax.security.enterprise.CallerPrincipal@317242d5] -> decoded roles [] -> domain decoded roles [] -> realm mapped roles [] -> domain mapped roles []
11:47:16,068 TRACE [org.wildfly.security] (default task-4) Permission mapping: identity [javax.security.enterprise.CallerPrincipal@317242d5] with roles [] implies ("javax.security.jacc.WebResourcePermission" "/user/home.xhtml" "GET") = false
11:47:16,069 INFO [io.undertow.accesslog] (default task-4) [14/Apr/2022:11:47:16 +0100] "GET /user/home.xhtml HTTP/1.1" 403 68 - https HTTP/1.1
I suspect I am missing some config in the elytron subsystem, I have a custom security domain <security-domain>other</security-domain>
defined in jboss-web.xml
In undertow config I have an application-security-domain defined as such:
<application-security-domain name="other" security-domain="ApplicationDomain" enable-jaspi="true" integrated-jaspi="false"/>
In Elytron I have the ApplicationDomain configured as follows:
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
<realm name="local"/>
</security-domain>
With the default-permission-mapper as follows:
<simple-permission-mapper name="default-permission-mapper">
<permission-mapping>
<role name="user"/>
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
</permission-mapping>
</simple-permission-mapper>
I have also tried using the from-roles-attribute
role decoder but that doesn't seem to work
Essentially I am just trying to tell Wildfly that the user has logged in and has 'x' roles assigned...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好的,如果有人正在寻找答案,如果您使用的是
@customformauthenticationmechanismdefinition
使用自定义IdentityStore,则需要实现两个覆盖验证方法的IdentityStore(如我上面显示的一个)另一个通过覆盖getCallerGroups
方法和设置validatytype.provide_groups
。 2个身份存储不需要,只是不要覆盖验证类型,它可以同时完成
Ok in case anyone is looking for the answer to this, if you are using
@CustomFormAuthenticationMechanismDefinition
with a Custom IdentityStore you need to implement two IdentityStore's one that overrides the validate method (like the one I have shown above) and another that assigns the roles/groups by overriding thegetCallerGroups
method and settingValidationType.PROVIDE_GROUPS
....then the roles are assigned to the principal and all is goodUPDATE: 2 identity stores not required, just don't override the ValidationType at all and it can do both
也许您缺少EJB3子系统的配置来将安全域名映射到Elytron安全域?
Maybe you are missing configuration of the EJB3 subsystem to map a security domain name to an Elytron security domain?