I'm trying to do a basic spring security D/B authentication program.I tried this by two ways i.e.
Method 1 : Using custom tables for Spring Security authentication.
Method 2 : Using Spring security specific database tables for user authentication and authorization.
File Locations:
1. index.jsp -> webapp/index.jsp
2. welcome.jsp -> webapp/pages/welcome.jsp
3. login.jsp -> webapp/pages/login.jsp
For method 1,Spring security was not intercepting request and i didn't see errors in console.Instead of intercepting the request i was directly taken to welcome.jsp.
P.S - Since i was not trying authorization, i didn't use 'authorities-by-username-query' attribute below in security context xml. I'm not sure if its mandatory to create a table for authorization as well.
Below is my security-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
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.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/welcome.html" />
<security:form-login login-page="/login.html"
default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
<security:logout logout-success-url="/logout.html" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?" />
</security:authentication-provider>
</security:authentication-manager>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web- app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>SpringPOC</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContextDirect.xml
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
BaseController
//#RequestMapping(value="/login", method = RequestMethod.GET)
#RequestMapping("/login")
public ModelAndView login(Model model) {
//System.out.println("Inside /login...");
return new ModelAndView("login");
}
/*public String login(ModelMap model) {
System.out.println("Inside /login...");
return "login";
}*/
#RequestMapping(value="/loginfailed", method = RequestMethod.GET)
public String loginerror(ModelMap model) {
model.addAttribute("error", "true");
return "login";
}
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {
return "login";
}
login.jsp
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.errorblock {
color: #ff0000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
<h3>Login with Username and Password (Authentication with Database)</h3>
<c:if test="${not empty error}">
<div class="errorblock">
Your login attempt was not successful, try again.<br /> Caused :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<form name='f' action="<c:url value='j_spring_security_check' />"
method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='j_username' value=''>
</td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='j_password' />
</td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="submit" />
</td>
</tr>
<tr>
<td colspan='2'><input name="reset" type="reset" />
</td>
</tr>
</table>
</form>
index.jsp
<body>
<div id="content">
<h1>Home Page</h1>
<p>
Anyone can view this page.
</p>
<p>Login page</p>
</div>
</body>
For method 2, i created spring specific database tables in the name of “USERS” and “AUTHORITIES” after following the below link. Here SQL query is not used in xml as shown below.
http://www.raistudies.com/spring-security-tutorial/authentication-authorization-spring-security-mysql-database/
Every thing remains same except for security-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
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.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<security:http realm="Project Realm" auto-config="true">
<security:intercept-url pattern="/welcome.html" access="ROLE_USER"/>
<security:form-login login-page="/login.html"
default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
<security:logout logout-success-url="/logout.html" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:jdbc-user-service data-source-ref="dataSource"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
when i tried the above way, even though i enter correct user name & password, i was getting 'bad credentials' message [But yes, in this case spring security is intercepting the request]. I'm using Oracle database.
[Update]: I enabled spring debug logging to find the root cause of errors in both methods. I couldn't figure out or understand what exactly is wrong from logs, so i compared logs i got after trying both methods.As,for method 1 Spring security was not intercepting request and for method 2 i was able to login (Spring security was atleast intercepting request) but i was getting 'Bad credential' message even after entering correct username & password.
Below is the code snippet for method 2[ Here i get login page,but authentication is failing]
firing Filter: 'FilterSecurityInterceptor'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/welcome.html'; against
'/welcome.html'
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL:
/welcome.html; Attributes: [ROLE_USER]
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated:
org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials:
[PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364:
RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter:
org.springframework.security.access.vote.RoleVoter#19432e0, returned: -1
DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter:
org.springframework.security.access.vote.AuthenticatedVoter#9830bc, returned: 0
DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous);
redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation
(AbstractSecurityInterceptor.java:206)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke
(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter
(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter
(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter
(SecurityContextHolderAwareRequestFilter.java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter
(BasicAuthenticationFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter
(AbstractAuthenticationProcessingFilter.java:183)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter
(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session:
DefaultSavedRequest[http://localhost:8080/itrade-web/welcome.html]
DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point.
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/itrade-
web/login.html;jsessionid=3FD72892F4F4EF2E65B0C90ABE115354'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents
are anonymous - context will not be stored in HttpSession.
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as
request processing completed
DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 1 of 10 in additional filter chain;
firing Filter: 'SecurityContextPersistenceFilter'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
firing Filter: 'UsernamePasswordAuthenticationFilter'
...
DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 7 of 10 in additional filter chain;
firing Filter: 'AnonymousAuthenticationFilter'
DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with
anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#6fa8940c: Principal:
anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1;
SessionId: 3FD72892F4F4EF2E65B0C90ABE115354; Granted Authorities: ROLE_ANONYMOUS'
...
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Request is to process
authentication
DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'admin'
DEBUG: org.springframework.security.authentication.dao.DaoAuthenticationProvider - User 'admin' not found
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request
failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder
to contain null Authentication
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication
failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#1882c1a
DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to
/loginfailed.html
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents
are anonymous - context will not be stored in HttpSession.
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as
request processing completed
[Update] For method 1 ,i added 'authorities-by-username-query' tag after creating a custom table for 'authorization'. Now i'm getting login screen, so i got to know inorder for spring security to intercept i need to have 'authorities-by-username-query' tag .But after entering user name and password i get following error mesage :
Caused : PreparedStatementCallback; uncategorized SQLException for SQL [select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state [null]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
i see following lines in debug mode :
DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: PreparedStatementCallback; uncategorized SQLException for SQL [select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state [null]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder to contain null Authentication
DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#e7736c
DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to /loginfailed.html
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'
[Update]: Now for both methods i'm getting same error though i enter correct user name & password.Also,since i could fetch data from D/B i'm sure that I'm not going wrong because of data not present in D/B.
DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'user'
I feel there should be any other reason behind this error.
[Edit] Now I've following 'users_detail' table in D/B :
USER_ID INTEGER
USERNAME VARCHAR2 (50 Byte)
PASSWORD VARCHAR2 (50 Byte)
ENABLED INTEGER
Data in the 'users_detail' table :
USER_ID USERNAME PASSWORD ENABLED
100 user 123456 1
My query is in security-context.xml :
"select username,password, enabled from users_detail where username=?"
when i execute the query manually i.e. select username,password,enabled from users_detail where username='user'. i get the resultsets.
Where am i going wrong ? Why is it that JdbcUserDetailsManager class always return 'Query returned no results for user 'user' ' even though there is an entry for the same in D/B.
Debug mode doesn't show which method of JdbcUserDetailsManager class is being executed when i get the above error. How can i know that? Also, does spring internally do any encryption/decryption technique while saving password field?
The log message "User 'admin' not found" seems pretty clear as a reason for authentication failure, when using the default schema. Why not just execute the command manually and see if it returns the user data?
Also, whether the login screen is shown doesn't depend on whether you set "'authorities-by-username-query" or not. It only depends on whether what intercept-url values apply for the URL you request. The only exception would be if you have customized the access-denied behaviour (for an authenticated user with insufficient rights) to show the login page (not the case here).
Your SQL exception is probably due to your custom table having the wrong column types. You need to end up with something compatible with the result set obtained from the standard schema. Better to stick with the default unless you have a good reason not to.
Better still, forget about Oracle completely until you can get the basics working with a simple test database like HSQLDB.
Related
I am trying to implement custom authentication using Spring security in my application by implementing AuthenticationProvider. The authentication is successful and the user also has the specified role but still I am always getting access denied page. Below is my code. i am new to spring security. Please help. Thanks in advance
Spring-security.xml
<form-login
login-page="/login" login-processing-url="/j_spring_security_check" default-target-url="/welcome" authentication-failure-url="/login?error"
/>
<access-denied-handler error-page="/403" />
<logout logout-success-url="/login?logout" />
<csrf disabled="true"/>
</http>
<authentication-manager id="dao-auth" erase-credentials="false">
<authentication-provider ref="customAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<b:bean id="customAuthenticationProvider" class="com.xom.custom.dataservice.impl.CustomAuthenticationProvider"></b:bean>
CustomAuthenticationProvider
#Override
public Authentication authenticate(Authentication authentication) throws
AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
final User rasUser;
try {
rasUser = checkPrivileges(name, password);
} catch (NoRASUserLoginException exception) {
throw new ServiceException(0, "exception while retrieving user data " + exception);
} catch (SQLException exception) {
throw new ServiceException(0, "exception while retrieving user privilages " + name + exception);
}
// userValue = (UserDetails) rasUser;
if (rasUser == null)
throw new UsernameNotFoundException(name + " not found");
List<SimpleGrantedAuthority> auths = new
java.util.ArrayList<SimpleGrantedAuthority>();
for (String privilege : rasUser.getPermissions()) {
if (privilege != null && privilege.equalsIgnoreCase("RReportAdmin"))
{
auths.add(new
SimpleGrantedAuthority("ROLES_".concat(privilege)));
}
}
auths = auths.stream().distinct().collect(Collectors.toList());
authentication = new UsernamePasswordAuthenticationToken(name, password, auths);
return authentication;
}
Login.jsp
<html>
<head>
<title>Login</title>
</head>
<body onload='document.loginForm.username.focus();'>
<h1>Spring Security Custom Login Form (XML)</h1>
<div id="login-box">
<h3>Login with Username and Password</h3>
<form name='loginForm'
action="<c:url value='/j_spring_security_check' />" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username'></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="submit" /></td>
</tr>
</table>
</form>
</div>
</body>
</html>
logs
2017-11-07 03:47:42,212 DEBUG o.s.s.w.u.m.AntPathRequestMatcher [http-nio-8080-exec-15] Checking match of request : '/admin'; against '/admin'
2017-11-07 03:47:42,214 DEBUG o.s.s.a.i.AbstractSecurityInterceptor [http-nio-8080-exec-15] Secure object: FilterInvocation: URL: /admin; Attributes:[hasRole('ROLES_RReportAdmin')]
2017-11-07 03:47:42,214 DEBUG o.s.s.a.i.AbstractSecurityInterceptor [http-nio-8080-exec-15] Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#e68aaf8b:
Principal: rparwee; Credentials: [PROTECTED]; Authenticated: true;
Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#1c07a:
RemoteIpAddress: 127.0.0.1; SessionId:
EE3501D56ED257409E40A4F8D5F6F794; Granted Authorities:
ROLES_RReportAdmin
2017-11-07 03:47:42,216 DEBUG o.s.s.a.v.AffirmativeBased [http-nio-8080-exec-15] Voter:
org.springframework.security.web.access.expression.WebExpressionVoter#6102b9a6,
returned: -1
2017-11-07 03:47:42,219 TRACE o.s.c.s.AbstractApplicationContext [http-nio-8080-exec-15] Publishing event in WebApplicationContext for
namespace 'mvc-dispatcher-servlet':
org.springframework.security.access.event.AuthorizationFailureEvent[source=FilterInvocation:
URL: /admin]
2017-11-07 03:47:42,219 DEBUG o.s.s.w.a.ExceptionTranslationFilter [http-nio-8080-exec-15] Access is
denied (user is not anonymous); delegating to AccessDeniedHandler
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
Please try adding
access="permitAll" in Spring-security.xml for login-page="/login"
Also access="hasRole('ROLE_RReportAdmin')" in /welcome
I got my mistake. Spring check "ROLE" before checking for authorization. In my case i was adding "ROLES".
Code changed from intercept-url pattern="/admin**" access="hasRole('ROLES_RReportAdmin')" to intercept-url pattern="/admin**" access="hasRole('ROLE_RReportAdmin')"
I would like to prevent spring security from forwarding to the default "j_spring_security_check" after successful login,instead after successful login I would like to forward to custom welcome page "/auth/welcome.jsp" , attempted 2 approaches:
Diagram of my desired flow:
Approach # 1 (used xml configuration)
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>Toyota Relocation App</display-name>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- load spring security -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml,/WEB-INF/security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
</web-app>
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">
<!-- spring security should ignore "/" & "/resources/**" paths -->
<http pattern="/" security="none" auto-config="true"
use-expressions="true" />
<http pattern="/resources/**" security="none" auto-config="true"
use-expressions="true" />
<!-- spring security configuration -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/auth**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" authentication-failure-url="/login?error"
username-parameter="emailInputField" password-parameter="pinInputField"
default-target-url="/auth/welcome" always-use-default-target="true" />
<logout logout-success-url="/login?logout" />
</http>
<!-- user accounts -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="naim#test.com" password="12345" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
login.jsp: (relevant snippet)
<div class="mycontent">
<div id="logo_div">
<img src="resources/images/01_toyota_logo_small.png">
</div>
<div class="login_form_wrapper">
<c:if test="${not empty error}">
<div class="error">${error}</div>
</c:if>
<c:if test="${not empty msg}">
<div class="msg">${msg}</div>
</c:if>
<!-- content-->
<form name='loginForm' action="<c:url value='j_spring_security_check' />" method='POST'>
<!-- <FORM name='loginForm' action="auth" method="post">-->
<ul class="mylistview" data-role="listview">
<li>
<div><img id="email_input_icon" src="resources/images/01_login_email_icon_small.png"></div><div id="email_input" ><input name="emailInputField"" id="email_input_field" type="email" data-role="none" /></div></li>
<li>
<div><img id="pin_input_icon"src="resources/images/01_login_password_icon_small.png"></div><div id="pin_input" ><input name="pinInputField" id="pin_input_field" type="password" data-role="none" /></div></li>
<li>
<div id="login_button">
<button id="login" class="button" type="submit">LOGIN</button>
</div>
</li>
</ul>
</FORM>
</div>
HomeController:
package com.xyz.thedallasapp_poc;
//removed imports
#Controller
public class HomeControler {
public HomeControler() {
super();
// TODO Auto-generated constructor stub
}
/* serves the login landing/home page view*/
#RequestMapping("/")
public String printWelcome(ModelMap model) {
return "login";
}
/*process login request from login view */
#RequestMapping (value="/login**", method = RequestMethod.GET)
public ModelAndView processLogin(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
ModelAndView model = new ModelAndView();
model.addObject("previousView", "/login");
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("login");
return model;
}
/* forward /auth/welcome request to /auth/welcome.jsp*/
#RequestMapping(value = "/auth/welcome")
public ModelAndView authWelcome() {
ModelAndView model = new ModelAndView();
//if reffer is /login than view is auth/welcome
//else view is reffer
model.setViewName("/auth/welcome");
return model;
}
}//end controller
Result:
Successful authentication & failure happen correctly, however I'm forwarded "j_spring_security_check" view which after careful inspection is actually the previous view/page with a new URL , instead of /auth/welcome.jsp view.
The logs have no error.
Approach # 2 (used xml configuration & implemented AuthenticationSuccessHandler )
I implemented AuthenticationSuccessHandler in order to redirect to "auth/welcome.jsp after successful login.
The web.xml, HomeController.java are not changed compared to approach #1.
spring.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">
<!-- spring security should ignore "/" & "/resources/**" paths -->
<http pattern="/" security="none" auto-config="true"
use-expressions="true" />
<http pattern="/resources/**" security="none" auto-config="true"
use-expressions="true" />
<!-- spring security configuration -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/auth**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" authentication-failure-url="/login?error"
username-parameter="emailInputField" password-parameter="pinInputField"
default-target-url="/auth/welcome" always-use-default-target="true"
authentication-success-handler-ref="myAuthSuccessHandler"/>
<logout logout-success-url="/login?logout" />
</http>
<!-- user accounts -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="naim#test.com" password="12345" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<!-- custom bean used to override spring security page redirection after successful authentication -->
<beans:bean id="myAuthSuccessHandler"
class="com.hitachi.thedallasapp_poc.security.MyAuthSuccessHandler" />
</beans:beans>
**MyAuthSuccessHandler: **
package com.hitachi.thedallasapp_poc.security;
//removed imports
public class MyAuthSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
System.out.println("Class onAuthenticationSuccess request: "+request.getRequestURL());
System.out.println("Class onAuthenticationSuccess response status: "+response.getStatus());
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
protected void handle(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(authentication,request);
if (response.isCommitted()) {
return;
}
System.out.println("Class onAuthenticationSuccess ,request.getContextPath(): " + request.getContextPath());
System.out.println("Class onAuthenticationSuccess ,targetUrl: " + targetUrl);
redirectStrategy.sendRedirect(request, response, targetUrl);
}
/** check the Role of the user that was granted access, based on the role redirect to success page */
protected String determineTargetUrl(Authentication authentication, HttpServletRequest request) {
boolean isUser = false;
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
String redirectURL = null;
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
System.out.println("Class MyAuthSuccessHandler, grantedAuthority.getAuthority():" +grantedAuthority.getAuthority());
isUser = true;
break;
}
}//end for
//if user is USER_ROLE redirect to "/auth/welcome"
if (isUser) {
System.out.println("Class MyAuthSuccessHandler, request.getRequestURL():" +request.getRequestURL().toString());
redirectURL = "/auth/welcome";
}
//else redirect to default
else
{
redirectURL = request.getRequestURL().toString();
}
return redirectURL;
}
protected void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
}//end class MyAuthSuccessHandler
Result:
After successful login the user is still redirected to previous page but again with path "j_spring_security_check", rather than desired "auth/welcome.jsp"
Logs show no error.
My System.println results:
Class MyAuthSuccessHandler, grantedAuthority.getAuthority():ROLE_USER
Class MyAuthSuccessHandler, request.getRequestURL():http://theda
llasapp_poc/j_spring_security_check
Class onAuthenticationSuccess ,request.getContextPath(): /thedallasapp_poc
Class onAuthenticationSuccess ,targetUrl: /auth/welcome
Thanks any help would be appreciated.
I have a sprinc core + spring mvc + security up and running, using XML configuration and these are the versions I am currently using within my pom.xml:
<spring.version>4.0.1.RELEASE</spring.version>
<spring.security.version>3.2.0.RELEASE</spring.security.version>
So I decided to go ahead and move all my xml configuration to java based configuration. What happens is that the login-processing-url for Spring security stopped working and now gives me an error 405, saying that POST is not allowed. For instance, this is my spring security xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<!-- Satic content -->
<http pattern="/assets/**" security="none" />
<http pattern="/css/**" security="none" />
<http pattern="/img/**" security="none" />
<http pattern="/js/**" security="none" />
<!--http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/reference/htmlsingle/#ns-global-method-->
<global-method-security pre-post-annotations="enabled" />
<!-- Protected Requests -->
<http auto-config="true" use-expressions="true" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/login/**" access="permitAll" />
<intercept-url pattern="/api/**" access="permitAll" />
<intercept-url pattern="/**" access="isRememberMe() or isFullyAuthenticated()" />
<form-login login-processing-url="/loginCheck"
login-page="/login"
authentication-failure-url="/loginFailure"
default-target-url="/"
always-use-default-target="true"
password-parameter="password"
username-parameter="username"/>
<logout delete-cookies="JSESSIONID" logout-url="/logout" logout-success-url="/login" />
<access-denied-handler error-page="/403" />
<session-management invalid-session-url="/login" session-fixation-protection="newSession" />
<headers>
<frame-options/>
<xss-protection/>
</headers>
<remember-me services-ref="rememberMeServices" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="mongoDashAuthenticationProvider" />
<authentication-provider ref="rememberMeAuthenticationProvider" />
</authentication-manager>
<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg value="${security.app.key}" />
<beans:constructor-arg ref="userServiceImpl" />
<beans:constructor-arg ref="mongoDashPersistentTokenRepository" />
<beans:property name="tokenValiditySeconds" value="172800" />
<beans:property name="parameter" value="remember" />
<beans:property name="cookieName" value="REMEMBER_ME" />
</beans:bean>
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider" >
<beans:property name="key" value="${security.app.key}" />
</beans:bean>
<!-- Way better than SHA -->
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="15" />
</beans:bean>
and this is my java configuration file for spring security:
#Configuration
#EnableWebMvcSecurity
#PropertySource("classpath:app.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${security.app.key}")
String appKey;
#Autowired
UserService userService;
#Autowired
MongoDashPersistentTokenRepository mongoDashPersistentTokenRepository;
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(true)
.authenticationProvider(new MongoDashAuthenticationProvider())
.authenticationProvider(rememberMeAuthenticationProvider());
//.build();
/**
<authentication-manager alias="authenticationManager">
<authentication-provider ref="mongoDashAuthenticationProvider" />
<authentication-provider ref="rememberMeAuthenticationProvider" />
</authentication-manager>
*/
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/assets/**")
.antMatchers("/css/**")
.antMatchers("/img/**")
.antMatchers("/js/**")
.antMatchers("/fonts/**");
/**
<http pattern="/assets/**" security="none" />
<http pattern="" security="none" />
<http pattern="" security="none" />
<http pattern="" security="none" />
**/
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.xssProtection()
.frameOptions()
.and()
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/api/**").permitAll()
.antMatchers("/**").access("isRememberMe() or isFullyAuthenticated()")
.and()
.formLogin()
.loginProcessingUrl("/loginCheck")
.loginPage("/login")
//.failureUrl("/loginFailure")
.defaultSuccessUrl("/", true)
.passwordParameter("password")
.usernameParameter("username")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.and()
.sessionManagement()
.invalidSessionUrl("/login")
.sessionFixation().newSession()
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.rememberMe()
.rememberMeServices(rememberMeServices());
}
#Bean
public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
RememberMeAuthenticationProvider provider = new RememberMeAuthenticationProvider(appKey);
return provider;
/**
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider" >
<beans:property name="key" value="${security.app.key}" />
</beans:bean>
**/
}
#Bean
public PersistentTokenBasedRememberMeServices rememberMeServices() {
PersistentTokenBasedRememberMeServices rememberMeServices = new PersistentTokenBasedRememberMeServices(appKey, userService,
mongoDashPersistentTokenRepository);
rememberMeServices.setCookieName("REMEMBER_ME");
rememberMeServices.setParameter("remember");
rememberMeServices.setTokenValiditySeconds(172800);
return rememberMeServices;
/**
<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg value="${security.app.key}" />
<beans:constructor-arg ref="userServiceImpl" />
<beans:constructor-arg ref="mongoDashPersistentTokenRepository" />
<beans:property name="tokenValiditySeconds" value="172800" />
<beans:property name="parameter" value="remember" />
<beans:property name="cookieName" value="REMEMBER_ME" />
</beans:bean>
**/
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(15);
return passwordEncoder;
/**
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="15" />
</beans:bean>
**/
}
}
And this is my AppConfig
#Configuration
#Import({SecurityConfig.class})
//#ImportResource("classpath:spring/security-context.xml")
#ComponentScan(basePackages = { "com.mongom" }, excludeFilters = { #ComponentScan.Filter(Controller.class),
#ComponentScan.Filter(Configuration.class) })
#PropertySource(value = { "classpath:app.properties" })
#EnableMBeanExport
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("validation", "messages");
return messageSource;
}
#Bean
public PropertiesFactoryBean properties() {
PropertiesFactoryBean ppc = new PropertiesFactoryBean();
ppc.setLocations(new Resource[] { new ClassPathResource("app.properties") });
ppc.setIgnoreResourceNotFound(false);
return ppc;
}
}
web.xml
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>MongoDASH</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.mongom.spring.AppConfig</param-value>
</context-param>
<servlet>
<servlet-name>mongodash</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.mongom.spring.WebConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mongodash</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 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>
<env-entry>
<description>JNDI logging context for this app</description>
<env-entry-name>logback/contextName</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>mongom</env-entry-value>
</env-entry>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>400</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/403</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>
Console output:
02/06 11:53:26.678 [http-bio-8080-exec-7] WARN o.s.web.servlet.PageNotFound Request method 'POST' not supported
02/06 11:53:50.023 [http-bio-8080-exec-5] WARN o.s.web.servlet.PageNotFound Request method 'POST' not supported
02/06 11:53:53.408 [http-bio-8080-exec-5] WARN o.s.web.servlet.PageNotFound Request method 'POST' not supported
02/06 11:53:55.504 [http-bio-8080-exec-5] WARN o.s.web.servlet.PageNotFound Request method 'POST' not supported
Login form:
<form class="form-signin" action="loginCheck" method="POST">
<h2 class="form-signin-heading">sign in now</h2>
<div class="login-wrap">
<input type="text" name="username" class="form-control" placeholder="User ID" autofocus value="guest" />
<input type="password" name="password" class="form-control" placeholder="Password" value="!Guest2014!" />
<label class="checkbox">
<input type="checkbox" name="remember" />Remember me
<span class="pull-right">
<a data-toggle="modal" href="#myModal"> Forgot Password?</a>
</span>
</label>
<button class="btn btn-lg btn-login btn-block" type="submit">Sign in</button>
</div>
</form>
Spring Security logs:
02/06 15:57:34.202 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: Ant [pattern='/assets/**'], []
02/06 15:57:34.202 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: Ant [pattern='/css/**'], []
02/06 15:57:34.203 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: Ant [pattern='/img/**'], []
02/06 15:57:34.203 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: Ant [pattern='/js/**'], []
02/06 15:57:34.203 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: Ant [pattern='/fonts/**'], []
02/06 15:57:34.264 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#28562791, org.springframework.security.web.context.SecurityContextPersistenceFilter#1f33b16a, org.springframework.security.web.header.HeaderWriterFilter#12504e0, org.springframework.security.web.csrf.CsrfFilter#7ff12373, org.springframework.security.web.authentication.logout.LogoutFilter#40e9e799, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#334362d9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#892b7c2, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#d0da1d8, org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter#5eba06ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#303fb547, org.springframework.security.web.session.SessionManagementFilter#a5ae1e7, org.springframework.security.web.access.ExceptionTranslationFilter#13883d5f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#52ecba8]
Spring security longs when posting login form:
02/06 17:04:33.642 [http-bio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher Checking match of request : '/logincheck'; against '/assets/**'
02/06 17:04:33.643 [http-bio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher Checking match of request : '/logincheck'; against '/css/**'
02/06 17:04:33.644 [http-bio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher Checking match of request : '/logincheck'; against '/img/**'
02/06 17:04:33.644 [http-bio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher Checking match of request : '/logincheck'; against '/js/**'
02/06 17:04:33.645 [http-bio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher Checking match of request : '/logincheck'; against '/fonts/**'
02/06 17:04:33.645 [http-bio-8080-exec-10] DEBUG o.s.security.web.FilterChainProxy /loginCheck at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
02/06 17:04:33.645 [http-bio-8080-exec-10] DEBUG o.s.security.web.FilterChainProxy /loginCheck at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
02/06 17:04:33.646 [http-bio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository HttpSession returned null object for SPRING_SECURITY_CONTEXT
02/06 17:04:33.700 [http-bio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#705ebc6e. A new one will be created.
02/06 17:04:33.701 [http-bio-8080-exec-10] DEBUG o.s.security.web.FilterChainProxy /loginCheck at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
02/06 17:04:33.701 [http-bio-8080-exec-10] DEBUG o.s.security.web.FilterChainProxy /loginCheck at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
02/06 17:04:33.702 [http-bio-8080-exec-10] DEBUG o.s.security.web.csrf.CsrfFilter Invalid CSRF token found for http://localhost:8080/mongodash/loginCheck
02/06 17:04:33.703 [http-bio-8080-exec-10] DEBUG o.s.web.servlet.DispatcherServlet DispatcherServlet with name 'mongodash' processing POST request for [/mongodash/403]
02/06 17:04:33.704 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping Looking up handler method for path /403
02/06 17:04:33.719 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
02/06 17:04:33.719 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
02/06 17:04:33.720 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
02/06 17:04:33.720 [http-bio-8080-exec-10] WARN o.s.web.servlet.PageNotFound Request method 'POST' not supported
02/06 17:04:33.721 [http-bio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
02/06 17:04:33.721 [http-bio-8080-exec-10] DEBUG o.s.web.servlet.DispatcherServlet Null ModelAndView returned to DispatcherServlet with name 'mongodash': assuming HandlerAdapter completed request handling
02/06 17:04:33.721 [http-bio-8080-exec-10] DEBUG o.s.web.servlet.DispatcherServlet Successfully completed request
02/06 17:04:33.722 [http-bio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
02/06 17:04:33.722 [http-bio-8080-exec-10] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter SecurityContextHolder now cleared, as request processing completed
My question is: Am I missing anything here? I see that the application is working fine as I can see the login page and resources (js, css, img) being shown on that page, its just the login-processing-url that, when invoked with POST thru the login form, it doesnt work.
Thank you
TL
edit #1: updated with login form and spring security logs
edit #2: updated with spring security logs when posting login information
Based upon the logs you posted, the issue appears to be that you are not including the CSRF token in the login request.
DEBUG o.s.security.web.csrf.CsrfFilter Invalid CSRF token found for http://localhost:8080/mongodash/loginCheck
This did not happen with the XML based configuration, because CSRF is not on by default with XML configuration (to be passive). Since there is no CSRF token in the login request, Spring Security is forwarding to the /403 error page (this is what is configured as the access denied page in your Java Config). It appears the MVC controller that processes /403 does not allow for HTTP POST and is logging the error message you see.
To fix this, first I would ensure that /403 can processes other methods. This will help to troubleshoot any other similar issues you might have (i.e. at that point you should get a proper error message rather than seeing the error that the method is not supported).
Second, you have a choice:
Include the CSRF token in all POST, PUT, DELETE, etc forms. This will ensure you are protected against CSRF attacks.
UPDATE: If you are using #EnableWebMvcSecurity and Spring's JSP tag library or Thymeleaf 2.1+ to render your forms then the CSRF token is included automatically for you. See the Include CSRF Token section of the reference for details about automatic inclusion and for examples of how to include the token manually.
Alternatively, you can disable CSRF protection. This is NOT recommended, but can help be a stop gap if you want to upgrade to Spring Security's Java Configuration.
You can read more about CSRF protection in the CSRF section of the reference.
A few additional items:
Is there a reason you are explicitly configuring the headers? The configuration you have specified disables some of the default headers that will assist in protecting your application. The explicit configuration does not include the cache control header.
UPDATE: For example, you have the following:
http
.headers()
.xssProtection()
.frameOptions()
.and()
.authorizeRequests()
If you simply remove the headers declaration, all the default headers are added by default.
http
.authorizeRequests()
For the XML configuration you can include the <headers/> element with no children to get all the default headers. This explicit setup for XML is necessary to remain passive.
More information about the headers can be found in the Security HTTP Response Headers section of the reference.
There is quite a bit of configuration you can safely remove from your code since the Java Configuration will default to these values. For example, the username and password parameter configuration is unnecessary since this is the default value.
I have the following code in spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http security="none" pattern="/openid.jsp" />
<security:http security="none" pattern="/logout.jsp" />
<security:http security="none" pattern="/success.jsp" />
<security:http auto-config="true"
authentication-manager-ref="authManager">
<security:intercept-url pattern="/**" access="ROLE_USER"/>
<security:openid-login login-page="/openid.jsp"
authentication-failure-url="/logout.jsp"
default-target-url="/success.jsp" >
</security:openid-login>
</security:http>
<security:authentication-manager id="authManager">
<security:authentication-provider>
<security:user-service>
<security:user name="spring" password="spring"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
Tomcat access logs do show that Google has authenticated the openid but still I hit the logout.jsp after allowing access from Google open id page.
127.0.0.1 - - [13/Dec/2012:22:55:44 +0530] "GET /SpringWebSecurityOpenID/getEmp.do/10 HTTP/1.1" 302 35
127.0.0.1 - - [13/Dec/2012:22:55:44 +0530] "GET /SpringWebSecurityOpenID/openid.jsp HTTP/1.1" 200 258
127.0.0.1 - - [13/Dec/2012:22:55:49 +0530] "POST /SpringWebSecurityOpenID/j_spring_openid_security_check HTTP/1.1" 302 35
127.0.0.1 - - [13/Dec/2012:22:55:50 +0530] "GET /SpringWebSecurityOpenID/j_spring_openid_security_check?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud&openid.response_nonce=2012-12-13T17cwcwc4rf32dwfdwGJ5oVQ_g&openid.return_to=http%3A%2F%2Flocalhost%3A8080%2FSpringWebSecurityOpenID%2Fj_spring_openid_security_check&openid.assoc_handle=AMlYA9UzE_QF5BKDYtD-k3_TbEdofnp7-43i9om-guRWh1TG5LhzEN7lzPyJ0IXzTjtNDbZz&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle&openid.sig=4MN6wuiKCWkuNwfwfwfd32dddqwg%3D&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%cwcwc34r2dwefreWzH1fBOWj5v4U&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid432432EdewedrUfrp0nP3AWzH1fBOWj5v4U HTTP/1.1" 302 35
127.0.0.1 - - [13/Dec/2012:22:55:50 +0530] "GET /SpringWebSecurityOpenID/logout.jsp HTTP/1.1" 200 102
pls. help in troubleshooting this issue. If I remove openid login page attribute and use default open id form generated by Spring then I can easily get authenticated and hit the requested URL.
Have update spring-security.xml code above. The exception seen in the logs is:
2012-12-16 11:26:32,430{HH:mm:ss} DEBUG [http-bio-8080-exec-4] (ConsumerManager.java:1788)
- Local signature verification succeeded.
2012-12-16 11:26:32,430{HH:mm:ss} INFO [http-bio-8080-exec-4] (ConsumerManager.java:1848)
- Verification succeeded for: https://www.google.com/accounts/o8/id?id=AItOawl2FdNxxWJLrUfrp0nP3AWzH1fBOWj5v4U
2012-12-16 11:26:32,433{HH:mm:ss} DEBUG [http-bio-8080-exec-4] (ProviderManager.java:152) -
Authentication attempt using org.springframework.security.openid.OpenIDAuthenticationProvider
2012-12-16 11:26:32,434{HH:mm:ss} DEBUG [http-bio-8080-exec-4] (AbstractAuthenticationProcessingFil
ter.java:340) - Authentication request failed: org.springframework.security.core.userdetails.UsernameNotFoundException: h
ttps://www.google.com/accounts/o8/id?id=AItOawl2FdNxxWJLrUfrp0nP3AWzH1fBOWj5v4U
2012-12-16 11:26:32,434{HH:mm:ss} DEBUG [http-bio-8080-exec-4] (AbstractAuthenticationProcessingFil
ter.java:341) - Updated SecurityContextHolder to contain null Authentication
2012-12-16 11:26:32,435{HH:mm:ss} DEBUG [http-bio-8080-exec-4] (AbstractAuthenticationProcessingFil
ter.java:342) - Delegating to authentication failure handlerorg.springframework.security.web.authentication.SimpleUrlAuth
enticationFailureHandler#e9927a
Do I need to add the user I want to authenticate in spring-security.xml. I was trying to allow anybody with valid openid to login. I don't have any list of users to authenticate.
For your interceptor url try this instead of a role.
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
I configured Spring Security with remember-me option.
<security:global-method-security secured-annotations="enabled" />
<security:http pattern="/login.html" security="none"/>
<security:http pattern="/signup.html" security="none"/>
<security:http pattern="/scripts/**" security="none"/>
<security:http pattern="/styles/**" security="none"/>
<security:http pattern="/images/**" security="none"/>
<security:http disable-url-rewriting="true" access-denied-page="/accessDenied.jsp">
<security:session-management>
<security:concurrency-control error-if-maximum-exceeded="false" max-sessions="10"/>
</security:session-management>
<security:form-login login-page="/login.html" login-processing-url="/login" authentication-failure-url="/login.html?login_error=1" default-target-url="/"/>
<security:intercept-url pattern='/**' access='ROLE_USER' />
<security:logout logout-url="/logout" logout-success-url="/"/>
<security:remember-me services-ref="rememberMeServices" />
</security:http>
and then the services itself:
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<property name="tokenRepository" ref="myTokenRepository" />
<property name="userDetailsService" ref="userDetailsService" />
<property name="key" value="myRememberMeKey" />
<property name="alwaysRemember" value="true" />
</bean>
<bean id="myTokenRepository" class="com.mytwitter.web.security.MyTokenRepository">
</bean>
I can see in my schema that tokens are inserted/updated/deleted in database. so this is not an issue.
but sign on fails:
2012-02-13 13:35:56,497 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Remember-me cookie detected - 2065616
2012-02-13 13:35:56,498 DEBUG [http-bio-8080-exec-5] org.springframework.data.mongodb.core.MongoTemplate - findOne using query: { "series" : "Ww2a8WsycNlGWxZRDubTnA=="} in db.collection: mytwitter.rememberMeTokens - 2065617
2012-02-13 13:35:56,500 DEBUG [http-bio-8080-exec-5] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.security.core.session.SessionRegistryImpl#0' - 2065619
2012-02-13 13:35:56,500 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Refreshing persistent login token for user 'aaaa', series 'Ww2a8WsycNlGWxZRDubTnA==' - 2065619
2012-02-13 13:35:56,510 DEBUG [http-bio-8080-exec-5] org.springframework.data.mongodb.core.MongoTemplate - calling update using query: { "series" : "Ww2a8WsycNlGWxZRDubTnA=="} and update: { "$set" : { "tokenValue" : "u0m7/ze3DpDInv27+JuPdQ==" , "date" : { "$date" : "2012-02-13T11:35:56.500Z"}}} in collection: rememberMeTokens - 2065629
2012-02-13 13:35:56,511 DEBUG [http-bio-8080-exec-5] org.springframework.data.mongodb.core.MongoTemplate - findOne using query: { "nickname" : "aaaa"} in db.collection: mytwitter.users - 2065630
2012-02-13 13:35:56,512 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Remember-me cookie accepted - 2065631
2012-02-13 13:35:56,512 DEBUG [http-bio-8080-exec-5] org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.RememberMeAuthenticationProvider - 2065631
2012-02-13 13:35:56,513 DEBUG [http-bio-8080-exec-5] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.security.core.session.SessionRegistryImpl#0' - 2065632
2012-02-13 13:35:56,514 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter - SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: 'org.springframework.security.authentication.RememberMeAuthenticationToken#5195b417: Principal: com.mytwitter.web.security.AuthUser#15880543; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_USER'; invalidating remember-me token - 2065633
org.springframework.security.authentication.BadCredentialsException: The presented RememberMeAuthenticationToken does not contain the expected key
at org.springframework.security.authentication.RememberMeAuthenticationProvider.authenticate(RememberMeAuthenticationProvider.java:64)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:102)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:182)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:173)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:679)
2012-02-13 13:35:56,529 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Interactive login attempt was unsuccessful. - 2065648
2012-02-13 13:35:56,529 DEBUG [http-bio-8080-exec-5] org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Cancelling cookie - 2065648
Actually what I do see as weird is that in line 2012-02-13 13:35:56,514 which prints Authentication object, SessionId is null. What am I missing?
The problem solved.
I should have add "key" property in remember-me parameter as well.
<security:remember-me services-ref="rememberMeServices" key="myRememberMeKey" />
and it should be the key with the same name as in "remeberMeServices" bean.