I need to know when tomcat accepts a login using realm authentication for a given context. I've been looking at the possible listeners available (ServletContextListener and ServletContextAttributeListener) but can't figure out how to be notified when a login occurs. This should also work when using tomcat single sign on for multiple contexts. Any ideas?
Unfortunately there's no standard/abstract way to hook on it using the Servlet API. You need either to write appserver specific logic or to implement a global Filter which checks the HttpServletRequest#getUserPrincipal() everytime. E.g.:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
Principal user = request.getUserPrincipal();
HttpSession session = request.getSession(false);
if (user != null && (session == null || session.getAttribute("user") == null)) {
request.getSession().setAttribute("user", user);
// First-time login. You can do your intercepting thing here.
}
chain.doFilter(req, res);
}
If you have access to the server configuration, you might try writing a LifecycleListener (http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/LifecycleListener.html), which are configured with the server (see the tomcat server config docs for your version).
Not sure if it will do the trick or not, but a good place to check.
Good luck.
Related
I am going through different class implementations of Spring Security. I know that we set the Authentication object into SecurityContext ThreadLocal object as:
UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(upat);
So, basically for each thread there is a separate copy of SecurityContext ThreadLocal object which holds the Authentication object for that thread. Fine till here. I have SessionCreationPolicy set to Stateless in my SecurityConfiguration as well. Below is the security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception
{
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
final CorsConfigurer<HttpSecurity> cors = http.csrf().disable().cors().configurationSource(source);
final ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry exp =
cors.and().authorizeRequests();
exp.antMatchers("/getJWTToken/**").permitAll()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/rest/**").authenticated();
exp.and().exceptionHandling()
.authenticationEntryPoint(authEntryPoint())
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
// Add a filter to validate the tokens with every request
http.addFilterBefore(authRequestFilter(), UsernamePasswordAuthenticationFilter.class);
}
But, I am confused about what does 'threads' mean here?
Do they mean, individual HTTP requests without having anything to do with session i.e. for each HTTP request there will be a new ThreadLocal Authentication object?
Or, is it specific to an HTTP Session? i.e. for a user's session, there will be only one thread and hence one Security Context?
I have these two doubts as well for both the above points.
For 1 above, if it changes with each request, then why do we need to check for Authentication object in each request's thread as below. I mean, if it is a different thread, there is no need of this. It will definitely be null. (The below if condition exists in the application I am referring to).
if( SecurityContextHolder.getContext().getAuthentication() == null ) {
if( jwtTokenUtil.validateToken(jwtToken, userObj) )
{
if( userObj == null )
{
response.setStatus(401);
return;
}
else
{
UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(userObj, null,userObj.getAuthorities());
upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(upat);
}
}
}
For 2 above, if I have SessionCreationPolicy as stateless in my Security Config class then again, there is no session, but different requests on different threads.
I may be wrong in my interpretation of threads(ThreadLocal SecurityContext) here.
Need help.
SecurityContextHolder, SecurityContext and Authentication Objects
By default, the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal’s request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.
Some applications aren’t entirely suitable for using a ThreadLocal, because of the specific way they work with threads. For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. SecurityContextHolder can be configured with a strategy on startup to specify how you would like the context to be stored. For a standalone application you would use the SecurityContextHolder.MODE_GLOBAL strategy. Other applications might want to have threads spawned by the secure thread also assume the same security identity. This is achieved by using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL. You can change the mode from the default SecurityContextHolder.MODE_THREADLOCAL in two ways.
The first is to set a system property, the second is to call a static method on SecurityContextHolder. Most applications won’t need to change from the default, but if you do, take a look at the JavaDoc for SecurityContextHolder to learn more.
Storing the SecurityContext between requests
In Spring Security, the responsibility for storing the SecurityContext between requests falls to the SecurityContextPersistenceFilter, which by default stores the context as an HttpSession attribute between HTTP requests. It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes
Many other types of applications (for example, a stateless RESTful web service) do not use HTTP sessions and will re-authenticate on every request. However, it is still important that the SecurityContextPersistenceFilter is included in the chain to make sure that the SecurityContextHolder is cleared after each request.
sessionManagement
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
will lead to Spring Security using a NullSecurityContextRepository, instead of the default HttpSessionSecurityContextRepository.
It is a simple implementation, in that it will simply not save anything to the HTTP Session and, for every request, create a completely new and empty SecurityContext, hence with no stored authentication etc.
UPDATE
That means, the below condition is always true if session policy is
stateless. if( SecurityContextHolder.getContext().getAuthentication()
== null )
Yes, you will get the authentication as null unless you have set it before the condition gets invoked. In case you are using the JWT token, you can verify the same as below and can set the security context.
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
...
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String resolveToken(HttpServletRequest request){
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
Without knowing where this if statement is happening, it's hard to comment on whether it's needless. If a request does not require authentication, the authentication may be null, but there may be other cases.
If a request does require authentication, then once your servlet is invoked, the authentication should not be null.
Threads are not tied to a given user session. With Servlets, a thread is assigned from a thread pool to each HTTP request.
The SecurityContextHolder is re-established for each request, either by pulling the existing authentication from the session or, in your case, from the request data.
My question is simple - how to implement login-logout in servlet jsp?
Following is the use case...
I have a users table in DB with email,username and password
I have a mapped bean object - User in java having email,username,password properties
Simply I want to login by validating email and password BUT
Once I login and then logout, when I click on back button, it should not retain the session.
It should not give any warning BUT simply should ask for login
If I copy-paste restricted resource's link, it should ask for login
What all solutions I've gone through...
Some say to implement tomcat security using roles and bla bla... BUt I think I should not set username, passwords in some tomcat config file. Bcz the details are in DB table
Some ask to implement no-cache, pragma bla bla... but never work
Back button disable is foolish thing
**
What Help I am expecting from you guys ...?
**
Is there any third-party API available to do this?
How things are implemented in production ready applications ?
Should I use JAAS, or any other security process for exactly above mentioned scenario OR WHAT
Please give me some hint or solution how I should proceed implementing production ready login-logout in servlet-jsp
I've searched on internet but end up with simple Login examples or tomcat security roles etc. No one gives the actual solution. ANd please don't say that this question is NOT RELATED TO this FORUM.
Thanks
This happens because browser caches the web pages that are being loaded,you can prevent it by using filters and telling browser not to cache the web pages like below.
doFilter method of Filter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
HttpSession session = request.getSession(false);//don't create if session doesn't exist.
if (session==null || session.getAttribute("username") == null) {
RequestDispatcher rd=request.getRequestDispatcher("login");//dispatch it to your desired page i.e login page
rd.forward(request, response);
} else {
chain.doFilter(req, res);
}
}
You should configure this filter inside web.xml or using Annotations for which url-patterns you want to filter.refer documentation for more details.
If you're using Tomcat then a good place to start is Tomcat Standard Realm Implementations.
It's important to remember that normal Java EE security authenticates users and authorises them using roles - even if you only have the one.
Once you have done that you can implement Logout by invoking a servlet which calls HttpServletRequest.logout() and then invalidates the HttpSession:
request.logout();
request.getSession().invalidate();
and then:
response.sendRedirect("some protected page");
which should resolve your back button problem and land back on the login page.
I'm having a JavaEE Website running on a cloud-platform.
Now I want to use two types of authentications:
Is from an SSO-System, which is well integrated in the platfrom and works very nicely.
Is the problematic part: I want to authorize a user from 1) for the time of a session, and give him access to a more restricted resource.
Some details
I get the user and his data from 1).
The user first has to ask for permission to 2), which can be denyed or granted. A user gets authorization from a service, which is outside of the scope of his servlet.
For this purpose I pass a User-POJO (with the session of this user as a member) to a service.
If the service grants the rights to this user, it will set an attribute to the user session:
userSession.setAttribute("auth", "granted");
To restrict access to that resource I use a Filter:
#WebFilter("/supersecret/*")
public class NiceFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
// check
if (session.getAttribute("auth") != "granted")
// redirect to login
else
chain.doFilter(req, res);
}
//...
While this is currently working, I feel that my solution is very sloppy.
Altering the user-session outside the scope of a servlet seems to be bad practice.
Adding an attribute to the session for security-purposes is probably not a good idea?
I'd rather want to use standard JavaEE-mechanisms, but most of them are already used for auth-method 1), like declaring login-config in the web.xml.
Any ideas for a more robust solution to this problem?
Thanks in advance :)
I'm using ServletRequestListener to attach to new requests, get a ServletRequest object and extract cookies from it.
I've noticed that only HTTPServletRequest has cookies but I haven't found a connection between those two objects.
Is it okay to use
HttpServletRequest request = ((HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest());
to retrieve the request while in a RequestInitialized method? (I do want to run on every request)
FYI - This is all done in a JSF 1.2 Application
This is not correct. The FacesContext isn't available in a ServletRequestListener per se. The getCurrentInstance() might return null, leading to NPE's.
If you're running the webapp on a HTTP webserver (and thus not some Portlet webserver for example), you could just cast the ServletRequest to HttpServletRequest.
public void requestInitialized(ServletRequestEvent event) {
HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
// ...
}
Note that a more common practice is to use a Filter for this since you can map this on a fixed URL pattern like *.jsf or even on specific servlets so that it runs only when the FacesServlet runs. You might for example want to skip cookie checks on static resources like CSS/JS/images.
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
// ...
chain.doFilter(req, res);
}
When you happens to be already inside the JSF context (in a managed bean, phaselistener or whatever), you could just use ExternalContext#getRequestCookieMap() to get the cookies.
Map<String, Object> cookies = externalContext.getRequestCookieMap();
// ...
When running JSF on top of Servlet API, the map value is of type javax.servlet.http.Cookie.
Cookie cookie = (Cookie) cookies.get("name");
Yes, you can do that. In Web scenarios, this will always be ok. If you want to be sure, you could do a check for the type first. (Good practice anyway):
if (FacesContext.getCurrentInstance().getExternalContext().getRequest() instanceof HttpServletRequest) {
...
By the way: Why do you have to use FacesContext? From where are you calling this code?
Short question: Is it possible to do a redirection, say when a user isn't logged in, when a page is rendered?
For that you should use a Filter.
E.g.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
((HttpServletResponse) response).sendRedirect("error.jsf"); // Not logged in, so redirect to error page.
} else {
chain.doFilter(request, response); // Logged in, so just continue.
}
}
Here I assume that the User is been placed in the session scope as you would normally expect. It can be a session scoped JSF managed bean with the name user.
A navigation rule is not applicable as there's no means of a "bean action" during a normal GET request. Also doing a redirect when the managed bean is about to be constructed ain't gong to work, because when a managed bean is to be constructed during a normal GET request, the response has already started to render and that's a point of no return (it would only produce IllegalStateException: response already committed). A PhaseListener is cumbersome and overwhelming as you actually don't need to listen on any of the JSF phases. You just want to listen on "plain" HTTP requests and the presence of a certain object in the session scope. For that a Filter is perfect.
Yes:
if(!isLoggedIn) {
FacesContext.getCurrentInstance().getExternalContext().redirect(url);
}
You can use a PhaseListener to specify when you want to do redirection.
In a PhaseListener try:
FacesContext ctx = FacesContext.getCurrentContext();
ctx.getApplication().getNavigationHandler()
.handleNavigation(ctx, null, "yourOutcome");