How JSP page should check authentication - java

I am new to web programming. I am asking a common pattern to do things like checking authentication. Here is the scenario:
The website has a login page for visitors. It will take username and encrypted password and sent them to server, then get either a error code (username/password doesn't match)or an auth key from the server. When the user logged in successfully, I want the website automatically jump to the main.jsp page that presents the main functionality of the website.
In this case, I want main.jsp check the user authentication. That is, I don't want such thing happens like user can directly open www.example.com/main.jsp, and if they did thing like this, I want to redirect them to login page.
So how could I pass authentication information across page, and how could I prevent user from directly accessing the main.jsp without login? Do I need to use session or anything?

you could try using filters:
Filter can pre-process a request before it reaches a servlet,
post-process a response leaving a servlet, or do both.
Filters can intercept, examine, and modify requests and responses.
NOTE: be sure to add a session attribute once your user is logged in, you can use that session attribute on the filter
on your login.jsp add:
session.setAttribute("LOGIN_USER", user);
//user entity if you have or user type of your user account...
//if not set then LOGIN_USER will be null
web.xml
<filter>
<filter-name>SessionCheckFilter</filter-name>
<filter-class>yourjavapackage.SessionCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionCheckFilter</filter-name>
<!--url-pattern>/app/*</url-pattern-->
<url-pattern>/main.jsp</url-pattern> <!-- url from where you implement the filtering -->
</filter-mapping>
SessionCheckFilter.java
public class SessionCheckFilter implements Filter {
private String contextPath;
#Override
public void init(FilterConfig fc) throws ServletException {
contextPath = fc.getServletContext().getContextPath();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (req.getSession().getAttribute("LOGIN_USER") == null) { //checks if there's a LOGIN_USER set in session...
res.sendRedirect(contextPath + "/login.jsp"); //or page where you want to redirect
} else {
String userType = (String) req.getSession().getAttribute("LOGIN_USER");
if (!userType.equals("ADMIN")){ //check if user type is not admin
res.sendRedirect(contextPath + "/login.jsp"); //or page where you want to
}
fc.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}

How JSP page should check authentication
It shouldn't. You should use Container Managed Authentication, and define the login/security in web.xml via URL patterns.
Added by Glen Best:
E.g. Add something like this to web.xml:
<security-constraint>
<display-name>GET: Employees Only</display-name>
<web-resource-collection>
<web-resource-name>Restricted Get</web-resource-name>
<url-pattern>/restricted/employee/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>Employee</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>

This also works for me
<filter>
<filter-name>SessionCheckFilter</filter-name>
<filter-class>yourjavapackage.SessionCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionCheckFilter</filter-name>
<!--url-pattern>/app/*</url-pattern-->
<url-pattern>/main.jsp</url-pattern> <!-- url from where you implement the filtering -->
</filter-mapping>
public class SessionCheckFilter implements Filter {
private String contextPath;
#Override
public void init(FilterConfig fc) throws ServletException {
contextPath = fc.getServletContext().getContextPath();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (req.getSession().getAttribute("LOGIN_USER") == null) { //checks if there's a LOGIN_USER set in session...
req.getRequestDispatcher("login.jsp").forward(req, resp); //or page where you want to redirect
} else {
String userType = (String) req.getSession().getAttribute("LOGIN_USER");
if (userType.equals("ADMIN")){ //check if user type is admin
fc.doFilter(request, response); it redirected towards main.jsp
}
}
}
#Override
public void destroy() {
}
}

How about using:
String username = request.getRemoteUser();

Related

Tomcat HTTPS only except 1 endpoint

I already configured my Connector in the server.xml with the redirection from 8080 to 8443, and set the security-constraint in web.xml with the appropriate sub-tags. It redirects properly, but I would like to ignore the HTTP access and use only HTTPS. So I do not need redirection or smthing like that. An external service requires HTTP access for an endpoint, I would like to enable only that endpoint over HTTP.
I tried to remove the Connector with 8080 port, but with this approach there is no chance to get the request over http.
If you disable http conection you will not have an access to your application over http.
So you can implement a filter that checks if the protocol of current request is HTTP and endpoint URL is allowed otherwise block the request.
In your web.xml you can declare following filter:
<filter>
<filter-name>blockHttpFilter</filter-name>
<filter-class>com.example.BlockHttpFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>urlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Implementation may be following:
public class BlockHttpFilter implements Filter {
private ServletContext context;
public void init(FilterConfig fConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
if(req.isSecure() && checkHttpEnpointPath(req)){
chain.doFilter(request, response);
} else {
HttpServletResponse res = (HttpServletResponse)response;
res.sendError(403);
}
}
public void destroy() {
//we can close resources here
}
}

Spring access page only when session exists

It is possible to set up #RequestMapping annotation to only allow viewing a page if a session exists? (The user is logged on.)
If you don't want to use spring-security for some reason and want to have your custom implementation to allow access only is session exists, then you could also write a RequestFilter that implements javax.servlet.Filter, and check if the request has a valid session then allow it to go through else show an error page. Here's an example.
public class RequestAuthenticationFilter implements Filter {
private static final Logger LOG = Logger.getLogger(RequestAuthenticationFilter.class);
protected static final List<String> ALLOWED_URL_LIST = Arrays.asList("/login.htm", "/400.htm", "/403.htm", "/404.htm", "/405.htm", "/500.htm", "/503.htm");
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse response, FilterChain filterChain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpSession session = request.getSession(false);
String url = (request.getRequestURI());
if(ALLOWED_URL_LIST.contains(url) || url.endsWith(".css") || url.endsWith(".js") || url.endsWith(".png")
|| url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".ttf") || url.endsWith(".woff")
|| url.endsWith(".csv")) {
filterChain.doFilter(request, response);
}
else if((null == session) || session.getAttribute("user") == null
|| StringUtils.isEmpty(((User) session.getAttribute("user")).getUsername().trim())) {
((HttpServletResponse) response).sendRedirect("/login.htm");
}
else {
filterChain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}
In this example the additional check is that if the request is for any image,js,css file then we skip the session check.
Once you have added the filter implementation, you will have to next make sure that all the requests that you want to validate with session go through this Filter. For that you will have to create a bean for this filter and then reference that bean in your web.xml
Here's what you will have to include in your web.xml. Here the is the name with which you create the bean for your filter. And the can be used for deciding which url's you want verified for session check with this filter
<filter>
<filter-name>requestAuthenticationFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>requestAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
You can set it up through Spring-Security fairly easily - for eg. consider a sample configuration from Spring-security site:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ROLE_ADMIN') and hasRole('ROLE_DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
here paths mapped with /resources is allowed for everybody(including anonymous users), everything else requires the user to have some role or atleast to have logged in.

my servlet mapping spoil my filter

In my program any url in the form of /Controller/* is redirected by my servlet mapping to Controller class.
I tried to add a filter for authantication, if user is not logged in and path is not /Controller/RegForm it's redirecting to /Controller/RegForm.
Problem is because my servlet mapping redirects to /Controller, filter always gets the /Controller as path.
How can I use both filter and the servlet mapping ?
This is my web.xml:
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/Controller/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>/Controller/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
my filter:
#WebFilter("/Controller/*")
public class AuthFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
// If you have any <init-param> in web.xml, then you could get them
// here by config.getInitParameter("name") and assign it as field.
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String path = ((HttpServletRequest) request).getServletPath();
if ((session != null && session.getAttribute("student") != null )||(excludeFromFilter(path))) {
chain.doFilter(req, res); // Log
}
else {
response.sendRedirect("/registration-war/Controller/RegForm"); // No logged-in user found, so redirect to login page.
}
}
private boolean excludeFromFilter(String path) {
if (path.equals("/Controller/RegForm")) {
return true; // add more page to exclude here
} else {
return false;
}
}
You use HttpServletRequest.getServletPath() which returns the servlet URL which is (according to your servlet mapping) "/Controller".
You want the path info not the servlet path:
Returns any extra path information associated with the URL the client sent when it made this request. The extra path information follows the servlet path but precedes the query string and will start with a "/" character.
So for example this will return "/RegForm" if your user requests the /Controller/RegForm page.
String pathInfo = HttpServletRequest.getPathInfo();

POST to GET Redirection

On jsp page After session expiration if a user performs some DB operations the Post url HTTP status shows as 302 which should redirect to my logout Page but as it's a GET call to logout it is not being redirected.
How to redirect from POST Http status 302 to GET or what can be the other way to achieve this?
Some comments/suggestion:
When the server responds with HTTP 302 additionally it also sends a Location header which browser reads and makes another GET request.
This is how it typically works. So please make sure that when you send HTTP 302 you also send a Location header in response and then rest of the things will be handled by the browser.
Please note that here I have take liberty to assume that you can change server side code which sends HTTP 302. Let me know if its otherwise.
You can use Session Filter like :
public class SessionFilter implements Filter {
private ArrayList<String> urlList;
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getServletPath();
boolean allowedRequest = false;
if(urlList.contains(url)) {
allowedRequest = true;
}
if (!allowedRequest) {
HttpSession session = request.getSession(false);
if (null == session) {
response.sendRedirect("index.jsp");
}
}
}
chain.doFilter(req, res);
}
}
and by making an entry in webl.xml
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>
net.viralpatel.servlet.filter.SessionFilter
</filter-class>
<init-param>
<param-name>avoid-urls</param-name>
<param-value>index.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Or hide the details via placing following entry in web.xml:
<error-page>
<error-code>302</error-code>
<location>/302ErrorPage.jsp</location>
</error-page>

Session ID not the same in my Java EE application

I've written a application with a custom login system. And then written my own security filter for it which sets the area that can be accessed. Yet I always get redirected to the login page and then to the index page with is the logged in home page. I have discovered that the session ID is different from when I login to when I try to use something that is restricted. Here is my code:
public class securtityFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
//To change body of implemented methods use File | Settings | File Templates.
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
// if there is no userBean, then they have not gone through
// the login, so kick them to the login page
if(null==req.getSession().getAttribute("username"))
{
((HttpServletResponse)servletResponse).sendRedirect("../Login.jsp");
System.out.println("Redirected - No session");
}
// otherwise, let them go to the page/resource they want
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Gone through Filter");
// System.out.println("In Filter Servlet: "+ req.getSession().getId());
}
public void destroy() {
//To change body of implemented methods use File | Settings | File Templates.
}
}
Here is my web.xml file:
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>filters.securtityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/add/*</url-pattern>
</filter-mapping>
In your login servlet you have
while (rs.next())
{
HttpSession session = request.getSession(true);
String tmp = rs.getString(1);
System.out.println(tmp);
session.setAttribute("username", tmp);
count++;
}
So if you have no username attribute in your session, it is because this code block is not being executed. I assume that you are looping through the results of a database query, so check whether the actual query that you are executing returns any results.
Try changing
if(null==req.getSession().getAttribute("username"))
to
HttpSession ses = req.getSession(false); // null if no current
if(null == ses ||
null == ses.getAttribute("username"))
so that it never creates a new session inside your filter. Let the login page create the sessions.

Categories

Resources