I have following code for spring security but it does not work. When I open log-in page and enter username/password which is admin#myproject.com / secret, following error message will be shown. Once username/password are entered following with be added to the address ?error=1, even if I remove it manually and refresh the page message does not go. Nothing is shown in console.
Your login attempt was not successful due to
Bad credentials.
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<beans:import resource='login-service.xml' />
<http auto-config="true" access-denied-page="/notFound.jsp"
use-expressions="true">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/member**" access="hasRole('ROLE_MEMBER')" />
<form-login login-page="/signin" default-target-url="/index"
authentication-failure-url="/signin?error=1" />
<logout logout-success-url="/login?logout" />
<csrf />
</http>
<authentication-manager>
<authentication-provider>
<user-service> <user name="admin#myproject.com" password="secret"
authorities="ROLE_ADMIN"/>
<user name="user#yahoo.com" password="secret" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
The form has following code, it seems like SPRING_SECURITY_LAST_EXCEPTIONis not empty even before submitting the form.
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
<font color="red"> Your login attempt was not successful due
to <br />
<br /> <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />.
</font>
</c:if>
<form id="form-login" role="form" method="post"
action="<c:url value='/j_spring_security_check' />"
class="relative form form-default">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
I am not sure why, but the same code returns following error now
Your login attempt was not successful due to
Authentication method not supported: GET.
You need to allow everyone to access your /signin page, even if he is not authenticated.
<intercept-url pattern="/signin" access="permitAll" />
I wrote this answer before the question was changed the first time, at a time where the question was (it is still the title): "Spring-security shows 'Bad Credentials' even before submitting the form"
<intercept-url pattern="/member**" access="hasRole('ROLE_MEMBER')" />
<user name="user#yahoo.com" password="secret" authorities="ROLE_USER"/>
Above configs have two different Role names ROLE_MEMBER and ROLE_USER
UPDATE
Since Authentication method not supported: GET, can you try allowing GET.
<bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:postOnly="false" />
And the following change is also required in web.xml
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Hope this helps
The SPRING_SECURITY_LAST_EXCEPTION stays in the session even if you refresh the page. You need check for the error parameter:
<c:if test="${(not empty param.error) && (not empty SPRING_SECURITY_LAST_EXCEPTION)}">
I guess your login controller method is doing redirect or forward, and then tries to send a HTTP GET request to the login URL with the user name and password as query parameters. It is generally considered bad practice to send credentials as URL parameters and that's why is not allowed. It be should send a HTTP POST instead.
If you will want to stay with GET, you could bypass the check by using a request wrapper which returns HTTP POST instead of HTTP GET for getMethod.
Updated answer from #tharingu_DG should work, but it is still technically equivalent to sending unencrypted authentication credentials since anyone who steals it can use it to authenticate.
Related
I have updated Spring security from 4.x to 5.x. Now, I have this situation where Spring security asks user to confirm logout. With message
Are you sure you want to log out?
below given image for the same.
I want to get rid of this step. How to get rid of logout confirmation ?
Objective : I want to logout and redirect on page where I came from.
The security.xml :
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<http auto-config="true" use-expressions="true">
<!-- isAnonymous() -->
<intercept-url pattern="/**/add/**" access="isAuthenticated()" />
<intercept-url pattern="/**/delete/**" access="isAuthenticated()" />
<intercept-url pattern="/**/update/**" access="isAuthenticated()" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="uzer64" password="{noop}123456" authorities="ROLE_USER" />
<user name="admin" password="{noop}admin" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
It is a CSRF feature to avoid logout request initiated by malicious javascript from another site.
Your request is GET: /logout and hence spring security wants to confirm it by user action such as click.
So to avoid it. Your logout request should be POST and contain valid _csrf token.
You can achieve it by using spring form tag with method post as given below
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
...
<form:form action="${pageContext.request.contextPath}/logout"
method="post" modelAttribute="AnyModelAttributePassedFromController">
<form:button value="submit"> Logout</form:button>
</form:form>
...
Or
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
...
<form:form action="${pageContext.request.contextPath}/logout"
method="post" modelAttribute="_csrf">
<form:button value="submit"> Logout</form:button>
</form:form>
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
It works for me.
You can try the below:-
<logout
logout-success-url="/anonymous.html"
logout-url="/perform_logout" />
and you can mention the url where you want to redirect.You can also add
delete-cookies="JSESSIONID" for deleting cookies.
So I have this in my spring config xml file.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/welcome/*" access="hasRole('ADMIN')" />
<!-- <intercept-url pattern="/login" requires-channel="https" /> -->
<!-- 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="emailId"
password-parameter="pwd" />
<logout logout-success-url="/login?logout"/>
</http>
The role is authenticated correctly at login. I have 2 questions:
What's the difference between pattern="/welcome/*", pattern="/welcome*" and pattern="/welcome/**"? When the pattern="/welcome/*", the login is successful and the user sees the page. In both the other options, the 403 Access Denied page appears. The user does have 'ADMIN' privileges)
How does Spring security process logout? I have the following code in my welcome.jsp file:
<c:url value="/logout" var="logoutUrl" />
<form action="${logoutUrl}" method="GET" id="logoutForm">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
<script>
function formSubmit() {
document.getElementById("logoutForm").submit();
}
</script>
<c:if test="${pageContext.request.userPrincipal.name != null}">
<h2>
User : ${pageContext.request.userPrincipal.name} | Logout
</h2>
</c:if>
and this in my controller:
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/login?logout";
}
The page redirects correctly and displays the "logout successful" page but if I change the URL to go to "/welcome" again, it shows me the page. Shouldn't it display the 403 - Access Denied page?
About Ant Path matchers in Spring Security
The main role of using Ant-style syntax that you've mentioned is to resolve what exactly paths are valid.
The mapping matches URLs using the following rules:
? matches one character
* matches zero or more characters
** matches zero or more directories in a path
Regarding your cases:
/welcome/* - this could be valid for URLs like /welcome/hello or /welcome/#hello, /welcome/?abc=123
/welcome* - valid are /welcome?abc=123, /welcome#abc=123.
/welcome/** - valid case is /welcome/hello/bye?abc=123.
More on this could be found at Spring Documentation.
Logout action
I assume that you are using xml-configuration for security. Anyway this could be modified for usage of pure Java-configuration.
In app-security.xml should be something like this below:
<http use-expressions="true"
disable-url-rewriting="true">
<http-basic />
<!-- other configurations -->
<intercept-url pattern="/login**" access="isAnonymous()"/>
<intercept-url pattern="/**" access="isAuthenticated()"/>
<!-- other configurations -->
<logout logout-url="/logout"
logout-success-url="/login"/>
</http>
And somewhere in index.html file:
<a href="<c:url value="/logout" />" id="item-btn-logout">
<i class="icon-off"></i> Logout
</a>
The most important part is URL: /logout.
Soory for disgusting you. Actually i am poor in english. I am developing a spring application by using google app engige.
1. If the server is in running mode, for the first if i would try to log into the application. Firstly it should navigate to the
login page.
2. If any user wants to access aby page in the application by giving the page name like for example(in my application if i have ABC.jsp page, if the user wnat to access the file by giving 127.0.0.0:8888/ABC.jsp) it should navigate to the some default page which contains some message, and click here to navigate to the home page.
Now can you please tell how to do it in my application.
can you please tell me the step by step process to achieve this by using Spring MVC ,Objectify ORM and Google app engine.
Still don't quite understand what your problem is.
Add this to your web.xml:
<welcome-file-list>
<welcome-file>/login</welcome-file>
</welcome-file-list>
</web-app>
This ensures that if a user only types http://server:port she is redirected to http://server:port/login.
Now if you want that the user is also redirected to login if she types http://server:port/foobar.html, what you call "random page", which does not exist then you need an HTTP status code mapping.
<error-page>
<error-code>404</error-code>
<location>/login</location>
</error-page>
If you want that the user must always first authenticate (i.e. go through /login) first before any existing page is displayed you could use Spring Security for that. Example for simple basic-auth:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<!-- HTTP basic authentication in Spring Security -->
<http>
<intercept-url pattern="/*" access="ROLE_USER" />
<http-basic />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="user" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
You can use spring security to do this. Have a look at
this link. The http tag should allow this. It will direct a user the login page for users that aren't authenticated. A sample context would be the following.
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/jsp/login.jsp" />
</beans:bean>
<http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
<intercept-url pattern="/protected" access="hasRole('ROLE_protected')" />
<intercept-url pattern="/jsp/login.jsp*" filters="none"/>
<logout logout-success-url="/jsp/login.jsp" invalidate-session="true" />
<session-management invalid-session-url="/jsp/login.jsp?timeout=true" />
</http>
I currently have a setup that looks something like this:
spring-security.xml:
<http auto-config="true">
<intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login"
default-target-url="/main.html"
authentication-failure-url="/failedLogin"/>
<logout logout-url="/logout.html" logout-success-url="/login" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="foo" password="bar" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
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>
This all seems to work as expected, however, in special situations I want the login page to be bypassed if the user passes in a special token. So currently, if the user goes to a url such as /dog, they will see the login page and if they pass in the credentials of foo/bar then they will be logged in and see the page corresponding to /dog.
I want the ability to use a URL such as /dog?token=abcd which will bypass the login screen and take them directly to the page corresponding to /dog. If they provide an invalid token then they would just see an access denied page.
In Spring Security the scenario you want to cover is described in reference manual, chapter Pre-Authentication Scenarios.
Basically you have to:
create custom filter by extending AbstractPreAuthenticatedProcessingFilter or choosing one of its implementations,
register custom filter <custom-filter position="PRE_AUTH_FILTER" ref="yourPreAuthFilter" />,
implement or choose one of implemented AuthenticationUserDetailsServices,
register the service in PreAuthenticatedAuthenticationProvider (with <property name="yourPreAuthenticatedUserDetailsService">).
EDIT: In this answer OP shows his way of implementig custom PRE_AUTH_FILTER.
I just setup Spring Security and it's working great. I can log in and out using my custom form. I have logged-in then out multiple times successively with no problem. Oddly, if I try to login with the wrong password, I can no longer log in again - it takes me to the "loginFailed" page every time I try to login after that. Does anyone know how to fix this?
Here is my security-config.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http use-expressions="true">
<intercept-url pattern="/secureA.htm" access="isAuthenticated()" />
<intercept-url pattern="/secureB.htm" access="isAuthenticated()" />
<form-login login-page="/login.htm" default-target-url="/secureA.htm"
authentication-failure-url="/loginFailed.htm"/>
<logout logout-success-url="/login" />
</http>
<authentication-manager>
<authentication-provider>
<password-encoder hash="sha">
</password-encoder>
<user-service>
<user name="user" password="pass"
authorities="ROLE_USER, ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
My login form:
<form action="<c:url value='j_spring_security_check' />" method="post">
Username: <input type="text" name="j_username" /><br/>
Password: <input type="password" name="j_password" /><br/>
<input type="submit" value="Login" /><br/>
</form>
My login controller:
#Controller
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String welcome(ModelMap m) {
return "login";
}
#RequestMapping(value = "/loginFailed", method = RequestMethod.GET)
public String failed(ModelMap m) {
m.addAttribute("error", "Invalid username or password");
return "login";
}
#RequestMapping("logout.htm")
public String logout(ModelMap m) {
return "redirect:login.htm";
}
}
If you're returning to the same page you logged into, the model is carried through. Since you're adding an attribute to that model, you're presumably checking to see if that attribute is set on the view page. Once it gets set, nothing will unset it until your session objects are cleared.
To confirm, change this:
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String welcome(ModelMap m) {
System.out.println(m.getAttribute("error");
return "login";
}
And then check your console.
Well, I don't know what the problem was, but I was able to resolve it. I just found an older SO post: Spring security blocks user after failed login. Timh's answer of switching to Spring 3.0.7.RELEASE (from 3.0.6) worked for me. Sorry for double-posting.
If anyone know why this was happening in 3.0.6, I'd be interested to know.
This is because:
https://jira.springsource.org/browse/SEC-1493
org.springframework.security.core.userdetails.eraseCredentials()
is being invoked on authentication.
Solution should be parameter erase-credentials="false" for authentication-manager, but apparently it doesn't worked for me in Spring 3.1.0 or 3.1.3.
Work-around would be to re-load users' details on each authentication request.