I was designated to implement a security requierement to mimic "messenger like" authentication, for example: if a user first logs in, then another user tries to log in with the same username, this new user will be prompted to "kick" the previously loged user and the system should invalidate the first user's web session, the web app is running on tomcat 6, i was taking a look at HTTPSessionContext but its now deprecated, is there an alternative or should i go and implement something myself using a HTTPSessionListener ?
HTTPSessionListener might not work because you don't have access to the user principal there. I have done something similar using a filter (without the invalidation of the session). This is a striped down version of what I did with some code that might work for your situation (haven't tested it):
public class ApplicationFilter implements Filter {
private Map<String, HttpSession> sessions = new HashMap<String, HttpSession>();
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Principal principal = request.getUserPrincipal();
HttpSession session = request.getSession();
if (principal != null && session.getAttribute("THE_PRINCIPAL") == null) {
// update the current session
session.setAttribute("THE_PRINCIPAL", session);
// get the username from the principal
// (assumes you using container based authentication)
String username = principal.getName();
// invalidate previous session if it exists
HttpSession s = sessions.get(username);
if (s != null)
s.invalidate();
// register this session as the one for the user
sessions.put(username, session);
}
chain.doFilter(servletRequest, servletResponse);
}
}
Related
I am doing up this application using mainly JSP and Tomcat as my server.
I am trying to set up authorization levels whereby certain class of users can do certain stuff(access certain pages like creating new records or searching past records), eg creating new users should only be done by an admin.
What i have done now is to first:
<%
String user = request.getParameter("name");
String pwd = request.getParameter("password");
String sql = "select * from members where name = ? and password = ?";
int role = 0;
// since execute returns an int of 1 or 0, we can use it for our if-else statement
if (BaseDAO.check(sql, user, pwd) != 0) {
session.setAttribute("user", user);
role = BaseDAO.checkRole(sql, user, pwd);
session.setAttribute("role", role);
response.sendRedirect("frameMgr.jsp");
} else {
session.setAttribute("login", 0);
response.sendRedirect("loginPage.jsp");
}
%>
After login is successful, I would then pull the value for role from the database and set it to session attribute. Then later at my createNewUser page, i have this to check if the user is of the assigned role
<%
int role = (Integer) session.getAttribute("role");
// only allow people with admin role to create more accounts
if (role != 5) {
response.sendRedirect("frameContent.jsp"); //back to homepage
}
%>
However i realised that this method is inefficient as i would have to put the check on every page and if there are changes in the future i would have to go page by page to change the code. is there a method to control all the authorization levels on one page alone? rather than having to do this on all my jsp files
Best you can do is to use HTTP filter. Every request going to be validated with your filter. Of course this will only prevent user to access resources(page/images etc.) But it does not serve as authorizer for your methods and user interactions.
#WebFilter("/*") Every resources
#WebFilter("/user/*") Resources under user folder
#WebFilter("/admin/*") Resources under admin folder
#WebFilter("/what/ever/*")
Example:
#WebFilter("/user/*")
public class UserFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
if (/*Check if user is logged*/) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
res.sendRedirect(req.getContextPath() + "/login.jsp");
}
}
#Override
public void destroy() {
}
}
Currently I use the following filter to redirect user to index page after session is expired.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession httpSession = httpRequest.getSession(false);
if (httpSession != null && !httpSession.isNew()) {
chain.doFilter(request, response);
} else {
httpResponse.sendRedirect(request.getServletContext().getContextPath() + PathManager.getPagePath("index"));
}
}
But can the session be expired when executing code in doPost/doGet methods? So on entering the filter the session is ok, but the session expires inside the servlet's doPost/doGet method.
If such a scenario can happen, what is the solution to redirect the user to the login page?
You can throw an exception/message to the client when session has expired and then have the client redirect the user to the login Page.
To detect if session has been expired you will need to get the session from the ServletRequest and if it is null then it is expired.
//false parameter is used to return the existing session. if expired then null is returned.
final HttpSession session = request.getSession(false);
I have set a session attribute in LoginSuccessHandler(extending CustomAuthenticationSuccessHandler) like
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
HttpSession session = request.getSession();
session.setAttribute("TICKET", "dummyVAL");
}
I want to retrieve the same value in LogoutSuccessHandler(extending SimpleUrlLogoutSuccessHandler).But when I do
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
HttpSession session = request.getSession();
session.getAttribute("TICKET");
}
I get null.How can I retrieve session value in login handler that I have set in successhandler.
after session invalidation spring-security will call logout-success-url so you will not find session here
so work around is call a logout url pattern from logout link
and use this
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(ModelMap model, HttpSession session) {
//** do some thing **//
return "redirect:/j_spring_security_logout";
}
I think "onLogutSuccess" method will be fired when your session gets destroyed. thus no value or parameter can be found in session.
And what you are doing with request.getSession() is that you are creating a new session which in turn returns null for the attribute as it does not exists for that session.
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.
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.