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><a href="welcome.html">Login page</a></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?
Best Answer
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.