问题
After logging in using HttpServletRequest.login(String, String)
, using the code below, on following requests I still get a Basic Authentication prompt. Why is the login
function not working in my configuration?
My endpoint:
@POST
@Path("login")
@Consumes(MediaType.APPLICATION_JSON)
public void login(@Valid LoginRequest loginRequest) {
try {
User user = userController.findUserByUsername(loginRequest.getUsername()).orElseThrow(NotFoundException::new);
httpServletRequest.login(loginRequest.getUsername(), loginRequest.getPassword());
log.info(securityContext); // not null now!
}
catch (ServletException e) {
throw new NotAuthorizedException(e.getMessage(), e, AuthenticationHeaderFilter.CHALLENGE);
}
}
And my jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.jboss.com/xml/ns/javaee
http://www.jboss.org/j2ee/schema/jboss-web_5_1.xsd">
<security-domain>MyRealm</security-domain>
</jboss-web>
And my web.xml
:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>MyRealm</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-constraint>
<display-name>Authenticated content</display-name>
<web-resource-collection>
<web-resource-name>Authentication required</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<display-name>Anonymous content</display-name>
<web-resource-collection>
<web-resource-name>Exclude from Security</web-resource-name>
<url-pattern>/api/me/login</url-pattern>
</web-resource-collection>
</security-constraint>
回答1:
Actually, the contract for HttpServletRequest#login does not mandate that the authenticated identity be remembered for the duration of the HTTP session (if one already exists), and certainly not that an HTTP session should be created upon successful authentication (for if one does not exist).
Technically speaking, the HttpServletRequest#login
call goes straight through to the identity store
(the method's Javadoc uses the term login mechanism
for that). An identity store is a kind of database that typically only performs the credential validation and does not have knowledge about its environment (i.e. doesn't know about HTTP sessions, or remote EJB context IDs, or JCA inflow security IDs of whatever).
The authentication mechanism
IS aware of its environment, and this one is invoked by calling HttpServletRequest#authenticate
. But, this would normally be expected to start an interaction dialog with the user when not being authenticated yet, not remember the authenticated identity in the session if the user happens to be authenticated (the fact this happens to work on JBoss seems more like a coincidence than something that is supposed to happen).
That all said, section 13.10 of the Servlet spec does allow containers to create an HTTP session:
Containers may create HTTP Session objects to track login state. If a developer creates a session while a user is not authenticated, and the container then authenticates the user, the session visible to developer code after login must be the same session object that was created prior to login occurring so that there is no loss of session information.
(emphasis mine)
But... it's not overly clear if this text is in regard to calling the login()
method or the authenticate()
one.
In short, this is one of the many small gaps in the Java EE security spec; it's just not defined how to programmatically do a login with a given username/password and explicitly say if you want or do not want that to be for the current request only or for the remainder of the HTTP session.
We hope to fix issues like this in the Java EE Security API (JSR 375) for Java EE 8.
回答2:
The answer is that after invoking httpServletRequest#login(String, String)
you should still invoke httpSevletRequest#authenticate(HttpServletResponse)
. My final, working code, is:
httpServletRequest.login(loginRequest.getUsername(), loginRequest.getPassword());
httpServletRequest.authenticate(httpServletResponse);
回答3:
As you want programmatic authentication, there is no need of <login-config>
in web.xml
来源:https://stackoverflow.com/questions/38896538/httpservletrequest-login-does-not-keep-logged-in-for-subsequent-requests