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.
Related
I know this question can be found with different solutions. But I am unable to get it working in my project.
We are sending mails to users which has link to perform some action in the application. When user click on url he should be redirect to login page if he is not logged in and after login should be navigated to the targeted URL.
I am trying to fix using CustomLoginSuccessHandler here is the code.:
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
// public CustomLoginSuccessHandler(String defaultTargetUrl) {
// setDefaultTargetUrl(defaultTargetUrl);
// }
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
HttpSession session = request.getSession(false);
if (session != null) {
String redirectUrl = (String) session.getAttribute("url_prior_login");
if (redirectUrl != null) {
// we do not forget to clean this attribute from session
session.removeAttribute("url_prior_login");
// then we redirect
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
Configurations I am using are :
#Bean
public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler(){
CustomLoginSuccessHandler successHandler = new CustomLoginSuccessHandler();
// SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
// successHandler.setUseReferer(true); getting NULL in the controller every time
// successHandler.setTargetUrlParameter("targetUrl"); this also doesnt work as browser is redirect to /login page and URL parameters are lost
return successHandler;
}
protected void configure(HttpSecurity http) throws Exception {
http
.logout().logoutUrl("/logout").deleteCookies("JSESSIONID").logoutSuccessUrl("/logoutSuccess")
.and()
.authorizeRequests()
.antMatchers("/privacyPolicy", "/faq", "/aboutus", "/termsofuse", "/feedback","/feedbackSubmit", "/contactSsm", "/resources/**", "/userReply", "/userReplySubmit", "/image", "/logoutExternal", "/closeit").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll();
// .and().exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());
}
Problem using this configuration is, If i request for url say 'http:localhost:8080/showPage' spring security is navigating to 'http:localhost:8080/login' and I am unable to capture anything from original URL. Same problem occurs when I try to use a custom variable targetUrl and using it in the same CustomLoginSuccessHandler.
Please let me know if am taking a wrong approach or something else is missing
Also tried using Custom EntryPoint but unable to redirect using my entrypoint.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint{
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
request.getSession().setAttribute("targetUrl",request.getRequestURL());
redirectStrategy.sendRedirect(request,response,request.getRequestURL().toString());
}
}
Controller :
#RequestMapping(value="/login")
public ModelAndView loginHandler(HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
String targetUrl = request.getParameter("targetUrl");
if(targetUrl!=null){ // targetUrl is always null as spring security is navigating to /login asd parameters are lost
request.getSession().setAttribute("url_prior_login",targetUrl);
}
mav.setViewName("login");
return mav;
}
To login, page is navigated to a different domain. and I pass a redirect URL to that domain after successful login it redirects the page back to the redirecturl
<a href="https://domain/sso/identity/login?channel=abc&ru=${externalUrl.applicationUrl}login" >Sign In</a>
Spring Security already stores the request using a RequestCache the default implementation HttpSessionRequestCache stores the last request in the HTTP session. You can access it using the SPRING_SECURITY_SAVED_REQUEST attribute name to get it from the session.
Doing something like this in your controller
public ModelAndView login(HttpServletRequest req, HttpSession session) {
ModelAndView mav = new ModelAndView("login");
if (session != null) {
SavedRequest savedRequest = session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
if (savedRequest != null) {
mav.addObject("redirectUrl", savedRequest.getRedirectUrl());
}
}
return mav;
}
Then in your JSP you can use the redirectUrl to dynamically construct your URL.
http://your.sso/login?url=${redirectUrl}
The final thing you need to do is to make /login accessible for everyone by adding it to the list which is protected by permitAll(). If you don't do this, you will get into a loop or the last request is overwritten and will always point to the login page.
.antMatchers("/privacyPolicy", "/faq", "/aboutus", "/termsofuse", "/feedback","/feedbackSubmit", "/contactSsm", "/resources/**", "/userReply", "/userReplySubmit", "/image", "/logoutExternal", "/closeit", "/login").permitAll()
You don't need any other custom classes like EntryPoints or AuthenticationSuccessHandler implementations.
However as you are using SSO it would be probably best to investigate a proper integration with the SSO solution instead of this hack with a login page.
You will at least have one problem : HttpSession session = request.getSession();.
getSession()
Returns the current session associated with this request, or if the request does not have a session, creates one.
You should use getSession(false) if you want a null return in case there is no session.
In your case you'll never get a null session.
I had the same issue and have solved it by using SavedRequestAwareAuthenticationSuccessHandler as a successHandler to make Spring handle the saved request that was requested before redirecting to login page when user is not logged.
In WebSecurityConfigurerAdapter:
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String LOGIN_PATH = "/login";
#Autowired
MyApplicationAuthenticationSuccessHandler myApplicationAuthenticationSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
// Set the default URL when user enters a non internal URL (Like https://my-application.com)
myApplicationAuthenticationSuccessHandler.setDefaultTargetUrl("/myapp/home");
http.authorizeRequests().antMatchers("/resources/**").permitAll().antMatchers(LOGIN_PATH).permitAll().antMatchers("/auto/**").authenticated()
.and().formLogin().loginPage(LOGIN_PATH).permitAll()
.successHandler(myApplicationAuthenticationSuccessHandler).and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl(LOGIN_PATH)
.invalidateHttpSession(true).deleteCookies("JSESSIONID").permitAll().and().sessionManagement().invalidSessionUrl(LOGIN_PATH);
}
}
In custom SavedRequestAwareAuthenticationSuccessHandler:
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
#Component("myApplicationAuthenticationSuccessHandler")
public class MyApplicationAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException {
try {
super.onAuthenticationSuccess(request, response, authentication);
} catch (ServletException e) {
// redirect to default page (home in my case) in case of any possible problem (best solution in my case)
redirectStrategy.sendRedirect(request, response, "/myapp/home");
}
}
}
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();
I have problems with AccessFilter in java web. When I am calling /main.jspx it redirect to the login.jsp. But when I am trying to log-in some error appeared
public class AccessFilter implements Filter {
private FilterConfig filterConfig;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) request).getSession();
HttpServletResponse res = (HttpServletResponse) response;
Client client = (Client) session.getAttribute("client");
if (client != null) {
chain.doFilter(request, response);
} else {
RequestDispatcher dispatcher = request.getRequestDispatcher(
ConfigurationManager.getInstance().getProperty(ConfigurationManager.LOGIN_PAGE_PATH));
dispatcher.forward(request, response);
}
}
#Override
public void destroy() {
this.filterConfig = null;
}
}
web.xml:
<filter>
<filter-name>AccessFilter</filter-name>
<filter-class>ua.kpi.shop.filter.AccessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AccessFilter</filter-name>
<url-pattern>/jsp/main.jspx</url-pattern>
<url-pattern>/jsp/pokemons.jspx</url-pattern>
</filter-mapping>
Error:
HTTP Status 404 - /PokemonsShop/login.jspx
type Status report
message /PokemonsShop/login.jspx
description The requested resource is not available.
filterConfig.getServletContext().getRequestDispatcher takes the absolute path as opposed to request-getRequestDispatcher. Though whether that is the solution I cannot say.
Two things came into my head when seeing your message:
1) Did you check whether the client object is null or not ? May be when executing the login action (method) you are not setting correctly the client into session ?
2) In the server error, it says "not found /PokemonsShop/login.jspx" but in your filter mapping you are mentioning /jsp/xxx. Would it be because your login page is under the folder jsp and you are redirecting (in the filter) to /PokemonsShop/login.jspx, which should be under webapp root folder to be accessible.
Hope one of them be of help
i want session Object not in servlet class but ordinary from we application.
WEB.XML
<listener>
<listener-class>com.abc.web.ApplicationManager</listener-class>
</listener>
<listener>
<listener-class>com.abc.web.SessionManager</listener-class>
</listener>
ViewPrices.java
public class ViewPrices implements Cloneable, Serializable {
Session session = request.getSession();
servletContext.getSession()
anyWay.getSession();
}
call this:
RequestFilter.getSession();
RequestFilter.getRequest();
on your custom filter:
public class RequestFilter implements Filter {
private static ThreadLocal<HttpServletRequest> localRequest = new ThreadLocal<HttpServletRequest>();
public static HttpServletRequest getRequest() {
return localRequest.get();
}
public static HttpSession getSession() {
HttpServletRequest request = localRequest.get();
return (request != null) ? request.getSession() : null;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (servletRequest instanceof HttpServletRequest) {
localRequest.set((HttpServletRequest) servletRequest);
}
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
localRequest.remove();
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
that you'll register it into your web.xml file:
<filter>
<filter-name>RequestFilter</filter-name>
<filter-class>your.package.RequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
There are multiple ways to do that, but.. don't. Only your web layer should have access to the session. The other layers should only get the parameters from the session that it needs. For example:
service.doSomeBusinessLogic(
session.getAttribute("currentUser"),
session.getAttribute("foo"));
The options that you have to obtain the request, and from it - the session in a non-servlet class, that is still in the web layer:
store the request in a ThreadLocal in a Filter (and clean it afterwards)
pass it as argument - either in constructor (if the object is instantiated on each request) or as method argument.
I don't think it's possible, to directly access session and request object. What you can do is pass the session and/or request object from servlet to a Java class either in some method or in constructor of the Java class.
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.