j_spring_security_check returning 404 on Tomcat but works on Jetty - spring

I have a basic Spring security setup with Form based login. When submitting my login action to j_spring_security_check it works on Jetty but fails with 404 code on Tomcat (7 & 8). I am using Spring 3.2.
So submitting to server:port/app/j_spring_security_check returns 404 response.
My security http setup looks like the following:
<security:http >
<security:form-login login-page="/login.jsp"
username-parameter="j_username"
password-parameter="j_password" />
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN"/>
<security:custom-filter position="PRE_AUTH_FILTER" ref="springAccessManagerAuthenticationFilter"/>
<security:intercept-url pattern="/j_spring_security_check" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:session-management></security:session-management>
<security:headers>
<security:cache-control/>
<security:xss-protection/>
<security:hsts/>
<security:frame-options/>
<security:content-type-options/>
</security:headers>
</security:http>
The login page looks like the following:
<%# include file="/WEB-INF/jsp/tags.jsp"%>
<%#page session="false" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Login</title>
</head>
<body id="login">
<form method="POST" action="<c:url value='j_spring_security_check' />"
id="loginForm" autocomplete="off">
Username : <input id="username" type="text" size="15" maxlength="60" name="j_username"><br><br>
Password : <input id="password" type="password" size="15" maxlength="60" name="j_password"><br><br>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<input value="Login" type="submit" id="submit" name="_eventId_nextpage">
</form>
</body>
</html>
My web.xml has the SpringSecurityFilterChain:
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

I finally figured this out as being a typo in web.xml on the default filter.
I had:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/logout</url-pattern>
<url-pattern>/j_security_check</url-pattern>
</servlet-mapping>
j_security_check should have been j_spring_security_check

Related

No mapping found for HTTP request with URI [/.../j_spring_security_check] in DispatcherServlet with name 'servlet-dispatcher'

Plop,
Spring version: 4.0.2.RELEASE
Spring Security Version: 4.0.2.RELEASE
DB PostgreSQL Version: 9.4-1202-jdbc42
I'm trying to acces to my home page with a secure connection using spring-security.
When I try to connect with login/password I've got this error:
WARNING: No mapping found for HTTP request with URI
[/web-client-smarteo/j_spring_security_check] in DispatcherServlet
with name 'servlet-dispatcher'
When I submit with log/pass it's get me to:
http://localhost:8080/web-client-smarteo/j_spring_security_check?username=alfacamp&password=alfacam&submit=&%24%7B_csrf.parameterName%7D=%24%7B_csrf.token%7D
And show
HTTP 404 The requested ressource is unvailable
Details of my sample:
UPDATE
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/dispatcher-servlet.xml
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<!-- Spring Security Filter -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>servlet-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servlet-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml
<mvc:annotation-driven />
<context:component-scan base-package="com.smarteo.laugustoni.*" />
[...]
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/vues/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
spring-security.xml
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/welcome**" access="hasRole('CUSTOMER')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login
login-page="/connection"
default-target-url="/welcome"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/connection?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/connection?logout" />
<!-- enable csrf protection -->
<csrf/>
</http>
<!-- Select users and user_roles from database -->
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query=
"select usr_name,usr_password from smarteo_user where usr_name=?"
authorities-by-username-query=
"select usr_name, usr_role from smarteo_user where usr_name =? " />
</authentication-provider>
</authentication-manager>
ConnectionController.java
package com.smarteo.laugustoni.controller;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.smarteo.laugustoni.services.User.IServiceUser;
#Controller
public class ConnectionController {
#RequestMapping(value={"/", "/welcome**"}, method = RequestMethod.GET)
public String defaultPage(ModelMap pModel)
{
return "connection";
}
#RequestMapping(value="/connexion", method = RequestMethod.GET)
public ModelAndView connection(
#RequestParam(value="error", required = false) String error,
#RequestParam(value = "logout", required = false)String logout)
{
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("connection");
return model;
}
Thanks for helping.
EDIT 1
connection.jsp
<%#page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%#page session="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<form name="loginForm" action="/j_spring_security_check">
<!-- TextBox Section -->
<div class="input-group visible">
<spring:message code="connection.label.account"/>
<input name="username" path="username" placeholder="Nom du compte" type="text" class="form-control" aria-describedby="basic-addon1"/>
<div class="alert alert-danger" role="alert"><form:errors path="username" cssclass="error"/></div>
</div><br />
<div class="input-group visible">
<spring:message code="connection.label.password"/>
<input name="password" path="password" placeholder="Mot de passe" type="password" class="form-control" aria-describedby="basic-addon1"/><br />
<div class="alert alert-danger" role="alert"><form:errors path="password" cssclass="error"/></div>
</div><br />
<!-- TextBoxSection -->
<!-- Button Section -->
<button name="submit" type="submit" class="btn btn-default visible">
<spring:message code="connection.button.label.connect"/>
</button><br />
<!-- Button Section -->
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
EDIT 2:
I'm now using:
<form name="loginForm" action="<c:url value='/login' />" method="POST" >
I had also change my spring-security.xml:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/welcome**" access="hasRole('CUSTOMER')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login
login-page="/login"
default-target-url="/welcome**"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" />
<!-- enable csrf protection -->
<csrf/>
</http>
And my ConnectionController.java
#RequestMapping(value = "/welcome**", method = RequestMethod.GET)
public String defaultPage()
{
return "home";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout)
{
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("connection");
return model;
}
#RequestMapping(value = "/403", method = RequestMethod.GET)
public ModelAndView accesssDenied() {
ModelAndView model = new ModelAndView();
//check if user is login
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
UserDetails userDetail = (UserDetails) auth.getPrincipal();
model.addObject("username", userDetail.getUsername());
}
model.setViewName("403");
return model;
}
I'm now getting State HTTP 405 - Request method 'POST' not supported when I'm trying to login
My problem was due to spring security version.
Indeed for 4.x you have to put the csrf in your form action.
My sources modified with the solution:
connection.jsp
<form name="loginForm" action="<c:url value='/login?${_csrf.parameterName}=${_csrf.token} }' />" method="POST" >
<!-- TextBox Section -->
<div class="input-group visible">
<spring:message code="connection.label.account"/>
<input name="username" path="username" placeholder="Nom du compte" type="text" class="form-control" aria-describedby="basic-addon1"/>
<!--<div class="alert alert-danger" role="alert"><form:errors path="username" cssclass="error"/></div>-->
</div><br />
<div class="input-group visible">
<spring:message code="connection.label.password"/>
<input name="password" path="password" placeholder="Mot de passe" type="password" class="form-control" aria-describedby="basic-addon1"/><br />
<!--<div class="alert alert-danger" role="alert"><form:errors path="password" cssclass="error"/></div>-->
</div><br />
<!-- TextBoxSection -->
<!-- Button Section -->
<input name="submit" type="submit" class="btn btn-default visible" value=<spring:message code="connection.button.label.connect"/> />
<br />
<!-- Button Section -->
</form>
spring-security.xml:
<http auto-config="true" >
<intercept-url pattern="/welcome**" access="hasRole('CUSTOMER')" />
<form-login login-page="/login"
default-target-url="/welcome"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/403" />
<!-- enable csrf protection -->
<csrf disabled="true"/>
</http>

Debugging LDAP with shiro in Tomcat

I have a web service, and for that I want to authenticate users.
I'm trying to have a form based authentication with org.apache.shiro.realm.ldap.JndiLdapRealm as my realm.
But when I enter the credentials nothing happens but redirecting to login page again. And there's nothing get printed on the log.
My Question is how can I debug the shiro to figure out what is happening?
Edit:
Here is my shiro ini file
[main]
authc.loginUrl = /login.jsp
authc.successUrl = /index.jsp
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = sAMAccountName={0},DC=aaa,DC=bbb
ldapRealm.contextFactory.url = ldap://aaa.bbb.ccc:3268
ldapRealm.contextFactory.systemUsername = admin
ldapRealm.contextFactory.systemPassword= password
securityManager.realms = $ldapRealm
[urls]
/login.jsp = authc
/logout = logout
/** = authc
Edit 2: my login jsp
<form name="loginform" method="post">
plain
<label for="username">Username:</label>
<input type="text" id="username" name="username" />
<br/>
<label for="password">Password:</label>
<input type="password" id="password" name="password" />
<br/>
<label for="rememberMe">Remember me:</label>
<input type="checkbox" id="rememberMe" name="rememberMe" value="true" />
<br/>
<input type="submit" value="Login" />
</form>
my web.xml
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

Spring Logout not working and redirecting

I have the following JSF Page:
<h:form>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<b:navBar brand="TEST" brandHref="#" inverse="true">
<p:lightBox styleClass="menucolored">
<p:commandLink id="logout"
type="button"
action="/j_spring_security_logout"
value="Log Out" ajax="false"
styleClass="menucolored"/>
</p:lightBox>
</b:navBar>
</h:form>
Spring Security xml:
<http use-expressions="true" auto-config="true">
<form-login login-page="/pages/Login.xhtml"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/loginPage?error=1"
default-target-url="/pages/Home.xhtml"
always-use-default-target="true"
username-parameter="j_username"
password-parameter="j_password"/>
<!-- On logout success load the login page -->
<logout logout-success-url="/pages/Login.xhtml" />
<!-- enable csrf protection -->
<csrf />
</http>
Web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
I have tried replacing the command link with
<h:outputLink value="${request.contextPath}/j_spring_security_logout">logout</h:outputLink>
or
<h:outputLink value="${pageContext.request.contextPath}/j_spring_security_logout">logout</h:outputLink>
Still no luck. When I click the button nothing happens.
If CSRF enabled, you must logout with a POST method. You need to wrap it in a standard HTML form instead:
<form action="${request.contextPath}/j_spring_security_logout" method="post">
<input type="submit" value="Log out" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
For using a link instead of a button, you'll need to write some javascript in order to make the link submit the form. See this post.
I fixed it:
public String doLogout() throws ServletException, IOException {
System.out.println("In doLogout()");
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
.getRequestDispatcher("/logout");
dispatcher.forward((ServletRequest) context.getRequest(),
(ServletResponse) context.getResponse());
FacesContext.getCurrentInstance().responseComplete();
System.out.println("End doLogout()");
return null;
}
I had to use /logout instead of /j_spring_security_logout
You can use this javascript solution for Spring Security 4 + CSRF + JSF:
Logout
<form action="logout" id="logout" method="post">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>

Spring Security login won't work when dealing with 2 tomcat and nginx

I have two same webapps deployed on tomcat at 127.0.0.1:8090 and 127.0.0.1:8091. I want to distribute requests between these two apps, so I config the nginx like this:
upstream my_servers {
server 127.0.0.1:8090;
server 127.0.0.1:8091 backup;
}
server {
listen 80;
server_name *.testabcd.com;
charset utf-8;
location /myapp {
proxy_pass http://my_servers;
}
}
Everything worked fine then. So I moved forward, removed the "backup", the config now looks like this:
upstream my_servers {
server 127.0.0.1:8090;
server 127.0.0.1:8091;
}
server {
...
}
However, my app's login just wouldn't take any effect! I visit http://testabcd.com/my_servers/hello/ and it keep returning the login page!
Below is some details of my app:
My webapp is working with Spring Security 3.1.4, and the login configuration part in the applicationContext.xml is like this:
<sec:http auto-config="true" use-expressions="true">
<sec:intercept-url pattern="/login" access="permitAll"/>
<sec:intercept-url pattern="/logout" access="permitAll"/>
<sec:intercept-url pattern="/access_denied" access="permitAll"/>
<sec:form-login login-page="/login" default-target-url="/" authentication-failure-url="/access_denied"/>
<sec:logout logout-success-url="/logout"/>
<sec:intercept-url pattern="/hello/*" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/>
</sec:http>
My web.xml:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The login.jsp:
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# page isELIgnored="false" %>
<form id="form2" action="<c:url value='/j_spring_security_check'/>" method="post">
<label>email:</label>
<input type="text" id="j_username" name="j_username" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}"/><br/>
<label>password:</label>
<input type="password" id="j_password" name="j_password"/><br/>
<label>rememberme:</label>
<input type="checkbox" name="_spring_security_remember_me" />
<br/>
<input type="submit" value="Login" />
</form>
The controller:
#Controller
#RequestMapping("/")
public class HelloController {
#RequestMapping(value = "login")
public String login() {
return "login";
}
}
Have you tried to debug which backend servers the requests are actually being routed to? The nginx upstream module apparently uses round-robin load balancing, so what you're observing seems normal. You login and start a session on one backend server, and subsequently arrive at a different one which has no knowledge of the session (unless you are clustering sessions between them which I'd guess not).
Ideally you'll need to use a sticky-session load-balancer, based on the JSESSIONID cookie. I don't think nginx supports this out of the box, but you can set the ip_hash directive so that individual IP addresses always use the same backend server.

Call to j_spring_security_logout not working

I'm trying to setup the logut of my application with j_spring_security_logout but for some reason it's not working, I keep getting a 404 error.
I'm calling the function like this:
<img border="0" id="logout" src="./img/logout.png" />
I have in WebContent/jsp/ my application main page, and the login and logout pages are in WebContent/login/.
I've also checked this other post Problem with Spring security's logout but the solution given there is not working for me.
Here you can see my web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And here my spring-security.xml
<http auto-config="true">
<intercept-url pattern="/*" access="ROLE_USER" />
<form-login login-page="/login/login.jsp"
authentication-failure-url="/login/errorLogin.jsp"/>
<logout logout-success-url="/" logout-url="/login/logout.jsp" />
</http>
<beans:bean id="myAuthenticationProvider"
class="myapp.web.authentication.WSAuthenticationProvider">
</beans:bean>
<authentication-manager>
<authentication-provider ref="myAuthenticationProvider"/>
</authentication-manager>
Thanks in advance.
the logout-url refers to a virtual URL, you need not have any resource by that name. You can do either this:
<logout logout-success-url="/" logout-url="/j_spring_security_logout" />
and the link on your page like this
<c:url value="/j_spring_security_logout" var="logoutUrl" />
Log Out
OR this:
<logout logout-success-url="/" logout-url="/logout" />
and the link as follows:
<c:url value="/logout" var="logoutUrl" />
Log Out
You were mixing both thats why you were getting 404 error.
check whether csrf is enabled. If csrf enabled, need to use post method to logout, add csrf token as hidden field. then use JavaScript to post the form to logout
With spring security 4 Logout has to be done through form button. CSRF token has to be submitted along. j_spring_security_logout does not work any longer. After spending one day i got following to be working.
Step 1: In your JSP page
<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}" method="post">
<input type="submit" value="Logout"/>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
Step 2
<security:http use-expressions="true">
<security:form-login login-page="/login" authentication-failure-url="/login?error=true" />
<security:logout logout-success-url="/login" invalidate-session="true" logout-url="/logout" />
</security:http>
Step 3 In your login controller
//Logout mapping
#RequestMapping("/logout")
public String showLoggedout(){
return "logout";
}
Step 4 You must have one logout.jsp
Important to see that it will land onto login page after logout.
<security:form-login login-page="/login" authentication-failure-url="/login?error=true" />
So this login page must be there with corresponding mappping to login.jsp or whatever to map in your controller.
also heres what your controller should look like
#RequestMapping("/logout")
public String logoutUrl(){
return "logout";
}
first set security-context.xml the following code...
<security:logout logout-success-url="/"
invalidate-session="true" />
then add this code to your jsp file..
<script>
function formSubmit() {
document.getElementById("logoutForm").submit();
}
</script>
<c:url var="logoutUrl" value="/logout" />
Logout
</li>
<form action="${logoutUrl}" method="post" id="logoutForm">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
In JAVA-BASED Spring MVC config, you have to configure it in your security config class:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.servletApi().rolePrefix("");
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
This answer is doubled from, and is working on my case:
Spring Security Java Config not generating logout url

Resources