I have a JSF app and would like to have the user auto logout after a period of inactivity. Is there an standard way to do this?
Generally, the server (Tomcat, Glassfish...) that hosts the web application handles a timeout for a session.
For example, in Tomcat, you can define the session timeout for a particular web application by adding the folowing lines in the web.xml file:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
This will set the timeout to 30 minutes.
When a user does not send any request during a time greater that this defined timeout, the session on the server is invalidated. If the user tries to reconnect after the session has been invalidated, he will generally be redirected to another page or to an error page.
You can develop your own JSF Filter that will automatically redirect the user to a timeout.html page. Here is an example of such a filter :
public class TimeoutFilter implements Filter {
private static final String TIMEOUT_PAGE = "timeout.html";
private static final String LOGIN_PAGE = "login.faces";
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
HttpServletRequest requestHttp = (HttpServletRequest) request;
HttpServletResponse responseHttp = (HttpServletResponse) response;
if (checkResource(requestHttp)) {
String requestPath = requestHttp.getRequestURI();
if (checkSession(requestHttp)) {
String timeoutUrl = hRequest.getContextPath() + "/" + TIMEOUT_PAGE;
responseHttp.sendRedirect(timeoutUrl);
return;
}
}
filterChain.doFilter(request, response);
}
private boolean checkResource(HttpServletRequest request) {
String requestPath = request.getRequestURI();
return !(requestPath.contains(TIMEOUT_PAGE) || requestPath.contains(LOGIN_PAGE) || requestPath.equals(hRequest.getContextPath() + "/"));
}
private boolean checkSession(HttpServletRequest request) {
return request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
}
public void destroy() {
}
}
Related
I am building a mobile app and Restful API, I want the user of the app to be able to do GET what ever resources he want without Authentication. But if he want to do POST he have to enter his username and pass.
I already made a HTTP basic Authentication by putting a filter in web.xml.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.service.RestAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/webapi/*</url-pattern>
</filter-mapping>
and there are the classes
public class AuthenticationService {
ClientsService s = new ClientsService();
public boolean authenticate(String authCredentials) {
if (null == authCredentials)
return false;
// header value format will be "Basic encodedstring" for Basic
// authentication. Example "Basic YWRtaW46YWRtaW4="
final String encodedUserPassword = authCredentials.replaceFirst("Basic"
+ " ", "");
String usernameAndPassword = null;
try {
byte[] decodedBytes = Base64.decode(
encodedUserPassword);
usernameAndPassword = new String(decodedBytes, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
final StringTokenizer tokenizer = new StringTokenizer(
usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
boolean authenticationStatus =s.auth(username, password);
return authenticationStatus;
}
}
and the filter
public class RestAuthenticationFilter implements javax.servlet.Filter {
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
// better injected
AuthenticationService authenticationService = new AuthenticationService();
boolean authenticationStatus = authenticationService
.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
what i need to know is : how to pass the username and password or maybe just the id of the client to the methods of Restful after the Authentication.
A solution different to my comment: you can look for the HTTP request method and then make a decision if you call a authentication method or not:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
boolean authenticationStatus;
// check request method
if (((HttpServletRequest).request).getMethod().equals("GET")) {
authenticationStatus=true;
} else {
// better injected
AuthenticationService authenticationService =
new AuthenticationService();
authenticationStatus = authenticationService
.authenticate(authCredentials);
}
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
Update:
With JAX-RS a possible solution for obtaining more request information may look like this:
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
#GET
#Path("offers")
#Produces("application/xml")
public YourList getOffers(#Context HttpServletRequest request)
{
System.out.println("request to "+request.getRequestURI()+" , Auth: "+request.getHeader(AUTHENTICATION_HEADER));
// more stuff for obtaining data
}
*#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("INTERCEPTING---");
if(request !=null && request.getParameter("email")!=null){
System.out.println("session is NOT null------");
session = request.getSession();
session.setAttribute("user", request.getParameter("email"));
}else{
try{
if (session==null){
System.out.println("session lost");
}
}catch(IllegalStateException exception){
System.out.println("session expired");
"redirect:http://localhost:8080/web/authservice/fail";
/this is the redirection that i used, and i tried model and view as well but yet it does not work/
}
}
return true;
}
/*in the above method how should I redirect. Its not redirecting...............
Im unable to redirect the after the session expired it gives me a response commit exception. I ve been trying for hours no solution
Please provide a suitable solution */
Use a servlet filter instead, (place it according to you intercepet requirement in your web.xml).
public class CustomRequestFilter implements Filter {
WebApplicationContext springContext;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
springContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
}
#Override
public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException, LicenseException {
HttpServletRequest request = (HttpServletRequest) req;
if (XXX) {
//forward or redirect as per requirement
request.getRequestDispatcher("/WEB-INF/jsp/errors/SystemError.jsp").forward(request, response);
}
chain.doFilter(request, response);
}
}
I need to do some checks before every page is loaded to see if there's a need to redirect the user to another page (for security reasons).
When I was using JSF 2.0 I used a phase listener to do this job. Now that I'm using JSF 2.2 and all my beans are not JSF beans anymore, but CDI beans, I think I'm offered better choices to do this (or not?).
I've heard of the viewAction event, but I wouldn't like to be repeating metadata on every page (only if there's no other option).
So what's the best approach to implement this scenario in JSF 2.2 with CDI?
UPDATE (after #skuntsel suggestion)
This is the filter that I'm using for now. I would like to use it only after authentication to simplify its code. By the way, if you can see any mistake in it, I would appreciate if you told me.
#WebFilter("/*")
public class SolicitacoesFilter implements Filter
{
// I can't just use #Inject private User _user, because it needs to be initialized
// only when the user is authenticated. Otherwise an exception is thrown. If this
// filter was called only after the authentication I could use the mentioned code.
private User _user;
#Inject
private Instance<User> _userGetter;
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
if (initializeUser(request))
{
if (_user.isProvisoryPassword())
{
// Redirect to another page...
return;
}
if (_user.getStatus() != Status.ACTIVE)
{
// Redirect to another page...
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy()
{
}
private boolean initializeUser(ServletRequest request)
{
boolean userAuthenticated = ((HttpServletRequest) request).getUserPrincipal() != null;
if (userAuthenticated)
{
if (_user == null)
{
_user = _userGetter.get();
}
}
else
{
_user = null;
}
return _user != null;
}
}
Ok, what are the purposes of your redirection need ?
If it's about checking session User for authentification purposes, use filter:
Let's assume there is login form at : http://www.domain.com/login.jsf.
Once the user fires connect button, we want to redirect him to http://www.domain.com/member/welcome.jsf, and avoid other people not to access the member/welcome.jsf domain, I mean all the pages which are in http://www.domain.com/member/....
Here a simple design:
#WebFilter("/member/*")
public class SecurityCheck implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
if (session == null || session.getAttribute("User") == null) {
response.sendRedirect(request.getContextPath() + "/index.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
#Override
public void destroy() {
// Cleanup global variables if necessary.
}
Other case, use:
<h:link></h:link>,or <h:commandLink></h:commandLink> // Checking in the managed Beans method
You can also xml file, for redirection.
This question already has an answer here:
Servlet filter runs in infinite redirect loop when user is not logged in
(1 answer)
Closed 5 years ago.
I am getting an infinite loop with my filter. The url-pattern is not generic. I can't seem to figure why it is causing that. Here is the mapping for my filter
<filter>
<filter-name>AdminAuthentication</filter-name>
<filter-class>my.filters.AdminAuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AdminAuthentication</filter-name>
<url-pattern>/admin/addLocation</url-pattern>
<url-pattern>/admin/deleteLocation</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
This code is executed before chain.doFilter(request, response)
private void doBeforeProcessing(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (debug) {
log("AdminAuthFilter:DoBeforeProcessing");
}
HttpServletRequest _request = (HttpServletRequest) request;
HttpSession session = _request.getSession();
User user = (User) session.getAttribute("user");
if(user == null) {
//send redirect somewhere
HttpServletResponse _response = (HttpServletResponse) response;
_response.sendRedirect("login.jsp");
return;
}
}
My problem is when I go to admin/addLocation without logging in I get an infinite redirect like so
http://localhost:8080/PROJ/admin/admin/admin/admin... otherwise it works fine when I do login. The login.jsp is also not in the admin folder. Please help.
Your entry point needs to be outside of your filter. Your redirect is prob. fighting the chain.doFilter due to the fact user is null.
Here is a simple login filter I use to check if the user is logged in and in the session within the defined url pattern.
Filter descriptor
<filter>
<filter-name>AdminFilter</filter-name>
<filter-class>com.AdminLoginFilter</filter-class>
<description>Admin Login Filter</description>
<init-param>
<param-name>Admin_login_form</param-name>
<param-value>/administration/login</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<url-pattern>/administration/controlpanel/*</url-pattern>
</filter-mapping>
Servlet Filter
public class AdminLoginFilter implements Filter {
private FilterConfig filterConfig;
private String loginForm;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
loginForm = this.filterConfig.getInitParameter("Admin_login_form");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession();
ControlPanelUser adminUser = (ControlPanelUser) session.getAttribute(PageConstants.CONTROL_PANEL_USER);
if ((adminUser == null || adminUser.getBoId() < 1)) { //Send user to login form
filterConfig.getServletContext().getRequestDispatcher(loginForm).forward(request, response);
} else {// Send user to requested page
chain.doFilter(request,response);
}
}
public void destroy() {
this.filterConfig = null;
}
}
Credential check
public class CheckUserCredentialsCommand implements Command {
public void execute(CommandContext commandContext) throws Exception {
ILoginForm loginForm = new LoginForm();
loginForm.populateFromForm(commandContext);
List<ValidationMessage> messages = loginForm.validate();
if(messages != null && messages.size() > 0){
commandContext.setScopedVariable(PageConstants.LOGIN_MESSAGES, messages, ScopedContext.REQUEST);
} else {
ControlPanelUser customer = ControlPanelUserDAO.selectControlPanelUser(loginForm.getEmailAddress(), loginForm.getPasswrd());
if(customer != null){
commandContext.setScopedVariable(PageConstants.CONTROL_PANEL_USER, customer, ScopedContext.SESSION);
} else {
commandContext.setScopedVariable(PageConstants.LOGIN_MESSAGES, messages, ScopedContext.REQUEST);
}
}
String referer = commandContext.getRequest().getHeader("referer");
if(referer != null){
referer = referer.substring(referer.lastIndexOf("/") + 1, referer.length());
if("login".equals(referer)){
commandContext.redirect(commandContext.getServletContext().getContextPath()+"/administration/controlpanel/dashboard");
} else {
commandContext.redirect(commandContext.getRequest().getHeader("referer"));
}
} else {
commandContext.redirect(commandContext.getServletContext().getContextPath()+"/administration/controlpanel/dashboard");
}
}
}
my login entry is http://www.mysite.com/administration/login, when i login on that page it submits to the CheckUserCredentialsCommand which is just a simple servlet. That servlet then tries to do a page redirect to one of the pages that is behind the filter. In the filter it checks the user, if the user is null it forwards back to the login page, if there is a valid user it goes through the filter chain which was your redirect from the CheckUserCredentialsCommand and now your ur l looks like http://www.mysite.com/administration/controlpanel/dashboard, dashboard page being behind the filter, if there was no user you would never be able to get to that page.
I would like to track user's sessions. I am interested in getting the user logname, context he accessed and the time when he accessed a certain context.
I was thinking of using a class that implements HttpSessionListener (overriding sessionCreated(final HttpSessionEvent se) and sessionDestroyed(final HttpSessionEvent se)) but on these methods I don't get access to the request (from where I could pull the user's logname and context he accessed).
Any suggestions are welcome.
I think a servlet Filter is more suitable for what you want. I suggest to write a custom filter around all the urls you want to track.
In the doFilter() method you have access to the HttpServletRequest as needed. From the request object you can get HttpSession too.
Here is an example:
#WebFilter("/*")
public class TrackingFilter implements Filter {
private FilterConfig filterConfig;
#Override
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpSession session = request.getSession(false);
String loggedInUser = "Unregistered user";
//assuming you have a session attribute named user with the username
if(session != null && session.getAttribute("user") != null) {
loggedInUser = (String) session.getAttribute("user");
}
Date accessedDate = new Date();
filterConfig.getServletContext().log(
String.format("%s accessed context %s on %tF %tT",
loggedInUser, request.getRequestURI() ,
accessedDate, accessedDate)
);
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
See also: JavaEE6 tutorial section about filters.