I would like to implement a simple authentication in an JSF/Primefaces application. I have tried lots of different things, e.g. Dialog - Login Demo makes a simple test on a dialog, but it does not login a user or?
I also have taken a look at Java Security, like:
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/protected/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>REGISTERED_USER</role-name>
</auth-constraint>
</security-constraint>
With this everything under /protected is protected, but as far as i understand i need to define a realm in the server for authentication. I do not want to specify this in the server but just have a simple lookup in the database for it.
Is there a way to authenticate a user without defining something in the application server? Or anonther simple solution to authenticate and protect different pages?
Is there a way to authenticate a user without defining something in the application server?
This is a long and very thorny story. It often comes up as one the main points of criticism against Java EE.
The core of the story is that traditionally Java EE applications are supposed to be deployed with "unresolved dependencies". Those dependencies have to be satisfied at the application server, typically by someone who is not a developer, often by using some kind of GUI or console.
Security configuration is one of those unresolved dependencies.
If the security configuration is done at the application server, this is by definition always not portable, e.g. it has to be done in an application server specific way. It completely rules out using application domain models (e.g. a JPA entity User) for this authentication.
Some servers (e.g. JBoss AS) allow configuring their proprietary security mechanisms from within the application, and additionally allow for 'custom login modules' (the term is different for pretty much every server) to be loaded from the application as well. With some small hacks, this will allow the usage of application domain models and the same data source that the application itself uses for authentication.
Finally, there's a relatively unknown portable way to do authentication from within an application. This is done via the JASPIC SPI, also known as JASPI or JSR 196. Basically, this JASPIC seems to be what you are looking for.
Unfortunately, JASPIC is not perfect and even though it's a technology from Java EE 6 and we're almost at Java EE 7, at the moment support for JASPIC in various application servers is still sketchy. On top of that, even though JASPIC is standardized, application server vendors still somehow require proprietary configuration to actually make it work.
I wrote an article about JASPIC that explains the current problems in more detail: Implementing container authentication in Java EE with JASPIC
I have found a for me suitable and easy solution by simply using Web Filters. I have added a filter to web.xml like
<!-- Authentication Filter -->
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.example.filters.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/protected/*</url-pattern>
</filter-mapping>
The filter looks something like this
#WebFilter(filterName="AuthenticationFilter")
public class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Cookie[] cookies = ((HttpServletRequest)request).getCookies();
// Try to find a valid session cookie
if (cookies != null) {
String sessionId = null;
for (Cookie cookie : cookies) {
if ("sessionId".equals(cookie.getName())) {
sessionId = cookie.getValue();
}
}
// Check if we have a valid session
UserSession session = Backend.getInstance().getSessionGateway().getBySessionId(sessionId);
if (session != null) {
chain.doFilter(request, response);
return;
} else if (sessionId != null) {
// Remove the cookie
Cookie cookie = new Cookie("sessionId", null);
cookie.setMaxAge(-1);
((HttpServletResponse)response).addCookie(cookie);
}
}
// Problem due to relative path!!
// ((HttpServletResponse)response).sendRedirect("../login.xhtml");
RequestDispatcher rd = request.getRequestDispatcher("/login.xhtml");
rd.forward(request, response);
}
}
So i had just to implement a Bean that authenticates and sets the session cookie. I will add the user agent to have additional security but it basically works.
The only problem i have that i can not do a redirect, cause it is not using context path but just redirects to /index.xhtml instead of /my_app_context/index.xhtml
Related
This question already has answers here:
How to redirect to Login page when Session is expired in Java web application?
(9 answers)
Closed 1 year ago.
I'm creating a website with HTML/CSS/Javascript/JSP and Tomcat v10 Java Servlets and have built a login system that creates a HttpSession once the user signs in. Even though this dynamically removes the login button, there is nothing stopping that user from copying and pasting the url to the login page.
Is there an effective way to immediately redirect to a different html file if a session does not exist? I saw some other posts using PHP but I've never used that technology and was hoping there is a diff way.
As far as I know, url to the JSP is treated like a request to the server
You can use a Filter for it.
Filter is for pre and post processing a request, you can use it to check if inbound request have session or not
Something like this:
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
HttpSession session = httpRequest.getSession();
AdUser user = (AdUser)session.getAttribute("user");
if (user == null) {
httpRequest.setAttribute("errorMessage", "You must login first");
httpRequest.getRequestDispatcher("Authenticate.jsp").forward(request, response);
} else {
chain.doFilter(request, response);
}
Instead of creating homegrown filter you should utilize Java EE security API
Create security constraint that will only allow authenticated users to access given parts of the application. In that case server will make sure that unauthenticated users cannot access given pages.
You could use opensource OpenLiberty server which fully implements Java EE API, if Tomcat is not implementing that spec fully.
Something like (not complete code just a sample to give you an idea):
<!-- SECURITY CONSTRAINT -->
<security-constraint>
<web-resource-collection>
<web-resource-name>protected pages</web-resource-name>
<url-pattern>/html/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>AUTH_USER</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>file</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
....
I'm implementing role-based authorization in my application following the structure of the java ee 8 jwt security sample: https://github.com/javaee-samples/javaee8-samples/tree/master/security/jwt.
I have a back-end application with session-based security and JSF. Authentication and Authorization is managed by a WebFilter:
#WebFilter(urlPatterns = "/faces/*")
public class AuthorizationFilter implements Filter {
}
I have a REST api with JWT token-based security where I want to implement HttpAuthenticationMechanism for managing authentication and authorization.
I want these two different security mechanisms to live next to each other for personal interest and to prove that I can implement both ways. However, the HttpAuthenticationMechanism gets called everytime, also when browsing my JSF application. This leads to triggering of both mechanisms that are conflicting.
Is it possible to apply the HttpAuthenticationMechanism to only a certain url path? Like the urlPattern that's used in WebFilter? If so, how does one?
I want to use the HttpAuthenticationMechanism only to be triggered in my rest application:
#ApplicationPath("rest")
public class RestApplication extends Application {
public RestApplication() {
}
}
There is a method "isProtected" in HttpMessageContext which will set to true/false depending on the security-constraints in web.xml
public AuthenticationStatus validateRequest(HttpServletRequest request,
HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
if (!httpMessageContext.isProtected()) {
return httpMessageContext.doNothing();
}
...
So the first thing to do is check isProtected and only continue if the resource is supposed to be protected.
Now you can use security-constraint in web.xml as usual:
...
<security-constraint>
<web-resource-collection>
<web-resource-name>Restricted</web-resource-name>
<description>Declarative security tests</description>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>somegroup</role-name>
</auth-constraint>
<user-data-constraint>
<description>no description</description>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role>
<description>Somegroup</description>
<role-name>somegroup</role-name>
</security-role>
...
Now authentication will only be done for URLs below the /protected/* pattern, anything else will be available unprotected
Wondering why this is not addressed in most of the tutorials/examples out there...
Credits goes to this link which seems to be the only one describing this (althoug there is a a missing "NOT" in the Listing No 16)
https://www.informatik-aktuell.de/entwicklung/programmiersprachen/authentication-mit-java-ee-8.html
Please help me on the below item:
Where exactly custom valve used instead of a filter?
In our application, I have created tomcat 9 custom valve and trying to access user principal from the valve. but in the valve, it returns null. but we are able to access from the filter. we have used form-based authentication.
User Principal will be available in Custom valve or is it available on in fliter?
Code snippet is provided below:
public class ContextInitializerValve extends ValveBase {
public ContextInitializerValve() {
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
System.out.println("======================custom valve==============================");
Principal principal = request.getUserPrincipal();
}
}
Thanks in advance
You can think of valves as the equivalent for filters, but server-side, not on the application side. E.g. you'd deploy a valve to the server, and typically can't hot-deploy new versions of it without restarting the server. You could redeploy a filter by redeploying your application that contains the filter.
Both Valves and Filters are order-sensitive: When you use Valves (tomcat uses them for server-side authentication) and you are sure that a user is authenticated, but you don't get the principal, your valve seems to be running before tomcat's authentication valve. You can either make sure to have your valve configured in the correct order, or move the implementation to a filter, because filters always run after all valves have been running.
In principle, they're very similar. Filters are defined by the servlet spec, while Valves (being part of the server implementation) are defined by Tomcat.
Short question. How is it possible to execute servlet filters before any declarative security check is performed?
Long question. For my web application I'm trying to manage all my security needs using server declarative security: I have set a security constraint on <url-pattern>/secure/*</url-pattern> with <auth-method>FORM</auth-method> and <form-login-page>/sign-in.xhtml</form-login-page>.
In order to provide a (cookie-based) "remember me" function, I have set a servlet filter which intercepts each request, checks if the user is not logged in, checks if he can be logged in automatically (via cookie), eventually logs him in using servlet based login.
<filter-mapping>
<filter-name>CustomLoginFilter</filter-name>
<url-pattern>*.xhtml</url-pattern>
</filter-mapping>
Now, if the user opens his browser and connects to mysite.com everything just works. But if the user opens his browser and makes a straight request to something like mysite.com/secure/secret.xhtml I observe the following behaviour:
GET /secure/secret.xhtml
the backing beans of log-in.xhtml are instantiated (FacesContext is available)
the CustomLoginFilter.doFilter( ) gets called, (FacesContext is NULL)
This obviously hinders all the process. I cannot find any way to give precedence to my CustomLoginFilter over the server "declarative security filter" (or whatever); changing the web.xml order of declarations doesn't help too... any ideas? Thank you!
How is it possible to execute servlet filters before any declarative security check is performed?
That's not possible due to specification and security restrictions.
Your best bet is a hook on preRenderView event in the login page and use programmatic login by the new Servlet 3.0 HttpServletRequest#login() method.
E.g.
<f:event type="preRenderView" listener="#{authenticator.checkAutoLogin}" />
with something like
public void checkAutoLogin() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Cookie autoLogin = externalContext.getRequestCookieMap().get("autoLoginCookieName");
if (autoLogin != null) {
User user = decryptCookieValueAndExtractUser(autoLogin.getValue()); // Yes, it must be encrypted! Otherwise a too easy hack.
if (user != null) {
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
try {
request.login(user.getName(), user.getPassword());
String originalRequestURI = externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
externalContext.redirect(originalRequestURI != null ? originalRequestURI : "someDefaultIndex.xhtml");
} catch (ServletException e) {
// Login failed. It's up to you how to handle it.
}
}
}
}
Is there a way to only allow POST requests to j_security_check? I want to reject GETs.
I am using Form Based security and want to only allow Posts to j_security_check. If a login request is made via a GET, the request should be rejected.
Having been trying to do the same on a JBOSS(Tomcat) server due to security concerns of JAAS using GET methods I attempted various ways.
Using a web.xml security constraint on the url pattern /j_security_check to only use POST - This doesn't work for JAAS mechanism as it would for normal servlets.
Passing login details from the login page to an intermediate servlet which checked the request method and if not a GET then forwarding on to j_security_check. - This did'nt work and was over complicated.
Creating a Filter that would check the request method and only invoke on a POST message to j_security_check - This didn't work as JAAS is deeper in web container and is called before the filter mechanism.
Creating a Valve, which DOES get called before the JAAS.
By adding the following in the invoke method:
HttpServletRequest req = (HttpServletRequest) request;
if (req.getMethod().equals("GET")) {
log.warn("Someone is trying to use a GET method to login!!");
request.getRequestDispatcher("/login.jsp").forward(req, response);
throw new ServletException("Using a GET method on security check!");
}
This does work.
Yes you can reject the GET request. In the web.xml file in the security constraint section you can specifiy the http methods allowed. In the following xml the only method allowed for this security constraint is the POST method. j_security check will only allow the post method.
<security-constraint>
<display-name>Your security constraint</display-name>
<web-resource-collection>
<web-resource-name>Your resource name</web-resource-name>
<url-pattern>/The URL pattern</url-pattern>
<http-method>POST</http-method>
<web-resource-collection>
<security-constraint>
You would need to rephrase your question.
j_security check is typically used in the login page.
If you request a secured resource and you were not authenticated, you are automatically redirected to the login page (assuming the app is configured to use Form Based security)
If your resource should not be challenged for GET requests, follow what Doug has mentioned. For example, if you want to secure POST calls to myaccount (the pattern for a Servlet) then you would be redirected to the login page only when a HTTP Post is made while the GET request would be accepted even without a user authentication.
The implication is you want to allow authenticated users access to POST request while GET requests are permitted to everyone.
An alternative approach I am considering to implement:
Blocking all but POST requests to j_security_check in a reverse-proxy/loadbalancer like nginx/apache
E.g. on Apache 2.4 this works:
<LocationMatch ".*j_security_check">
AllowMethods POST
</LocationMatch>
If I would need more customization I could reimplement j_security_check with my own servlet using HttpServletRequest.login(...)