Hi,
I would write an article concerning the overridden of the token of Spring 3 Security. In this post, we will follow several points: creation of a simple Spring MVC Web application (controller, JSP pages,…), securization with Spring security via custom login form, override the authentication components (provider, token,…).
Creation of Spring MVC Web application
- Create a simple Dynamic Web Module in Eclipse named Spring3Security;
- Final structure of Eclipse project named Spring3Security;
- Add the following jars in the folder Spring3Security\war\WEB-INF\lib:
- activation-1.1.jar
- antlr-2.7.7.jar
- aopalliance-1.0.jar
- asm-3.3.1.jar
- cglib-2.2.jar
- commons-logging-1.1.1.jar
- javassist-3.8.1.GA.jar
- jstl-1.2.jar
- org.springframework.aop-3.0.5.RELEASE.jar
- org.springframework.asm-3.0.5.RELEASE.jar
- org.springframework.beans-3.0.5.RELEASE.jar
- org.springframework.context.support-3.0.5.RELEASE.jar
- org.springframework.context-3.0.5.RELEASE.jar
- org.springframework.core-3.0.5.RELEASE.jar
- org.springframework.expression-3.0.5.RELEASE.jar
- org.springframework.jdbc-3.0.5.RELEASE.jar
- org.springframework.jms-3.0.5.RELEASE.jar
- org.springframework.orm-3.0.5.RELEASE.jar
- org.springframework.oxm-3.0.5.RELEASE.jar
- org.springframework.web-3.0.5.RELEASE.jar
- servlet-api-2.5.jar
- spring-oxm-tiger-1.5.9.jar
- spring-security-acl-3.1.2.RELEASE.jar
- spring-security-config-3.1.2.RELEASE.jar
- spring-security-core-3.1.2.RELEASE.jar
- spring-security-core-tiger-2.0.7.RELEASE.jar
- spring-security-taglibs-3.1.2.RELEASE.jar
- spring-security-web-3.1.2.RELEASE.jar
- spring-support-2.0.8.jar
- spring-webmvc-3.0.5.RELEASE.jar
- Edit the web descriptor file web.xml, in order to add a section concerning the Spring context and Spring security configuration (http filter):
001
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
002
<
web-app
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
003
xmlns
=
"http://java.sun.com/xml/ns/j2ee"
004
xmlns:javaee
=
"http://java.sun.com/xml/ns/javaee"
005
xmlns:web
=
"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
006
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
007
version
=
"2.5"
>
008
009
<
display-name
>SPRING3SECURITY</
display-name
>
010
011
<
welcome-file-list
>
012
<
welcome-file
>index.jsp</
welcome-file
>
013
</
welcome-file-list
>
014
015
<
error-page
>
016
<
error-code
>403</
error-code
>
017
<
location
>/unauthorizedAccess.jsp</
location
>
018
</
error-page
>
019
<
error-page
>
020
<
error-code
>404</
error-code
>
021
<
location
>/unauthorizedAccess.jsp</
location
>
022
</
error-page
>
023
024
<!-- ##################################### -->
025
<!-- SPRING SECURITY -->
026
<!-- ##################################### -->
027
<
filter
>
028
<
filter-name
>springSecurityFilterChain</
filter-name
>
029
<
filter-class
>org.springframework.web.filter.DelegatingFilterProxy</
filter-class
>
030
</
filter
>
031
<
filter-mapping
>
032
<
filter-name
>springSecurityFilterChain</
filter-name
>
033
<
url-pattern
>/*</
url-pattern
>
034
</
filter-mapping
>
035
036
037
<
listener
>
038
<
listener-class
>org.springframework.security.web.session.HttpSessionEventPublisher</
listener-class
>
039
</
listener
>
040
041
042
<!-- Login failed error -->
043
044
<!-- MUST be first context param -->
045
<
context-param
>
046
<
param-name
>webAppRootKey</
param-name
>
047
<
param-value
>spring3security.root</
param-value
>
048
</
context-param
>
049
050
<!-- MUST be second context param -->
051
<
context-param
>
052
<
param-name
>log4jConfigLocation</
param-name
>
053
<!-- <param-value>../../etc/spring3security-log4j.properties</param-value> -->
054
<
param-value
>classpath:log4j.properties</
param-value
>
055
</
context-param
>
056
057
<!-- MUST be first listener -->
058
<
listener
>
059
<
listener-class
>org.springframework.web.util.WebAppRootListener</
listener-class
>
060
</
listener
>
061
062
<!-- MUST be second listener -->
063
<
listener
>
064
<
listener-class
>org.springframework.web.util.Log4jConfigListener</
listener-class
>
065
</
listener
>
066
067
<
listener
>
068
<
listener-class
>org.springframework.web.context.ContextLoaderListener</
listener-class
>
069
</
listener
>
070
071
072
073
<!-- ##################################### -->
074
<!-- SPRING MVC & SPRING CONTEXT-->
075
<!-- ##################################### -->
076
<
context-param
>
077
<
param-name
>webAppRootKey</
param-name
>
078
<
param-value
>Spring3Security.root</
param-value
>
079
</
context-param
>
080
081
<
listener
>
082
<
listener-class
>org.springframework.web.util.WebAppRootListener</
listener-class
>
083
</
listener
>
084
<
listener
>
085
<
listener-class
>org.springframework.web.context.ContextLoaderListener</
listener-class
>
086
</
listener
>
087
088
<
context-param
>
089
<
param-name
>contextConfigLocation</
param-name
>
090
<
param-value
>/WEB-INF/applicationContext.xml</
param-value
>
091
</
context-param
>
092
093
<
servlet
>
094
<
servlet-name
>Spring3Security</
servlet-name
>
095
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet</
servlet-class
>
096
<
load-on-startup
>1</
load-on-startup
>
097
</
servlet
>
098
<
servlet-mapping
>
099
<
servlet-name
>Spring3Security</
servlet-name
>
100
<
url-pattern
>*.do</
url-pattern
>
101
</
servlet-mapping
>
102
103
</
web-app
>
Spring MVC configuration
- First, we need to configure the Spring MVC controllers. For this, edit the file Spring3Security-servlet.xml in order to define the “web beans” here such as the Controllers and the URL mapping configurations. The Web beans defined in this file may reference “application beans” in the “root” web application context:
01
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
03
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
04
xmlns:p
=
"http://www.springframework.org/schema/p"
05
xsi:schemaLocation="http://www.springframework.org/schema/beans
07
08
09
<!-- ####################### SPRING MVC ################################################### -->
10
<!--
11
Define the "web beans" here such as the Controllers and the URL mapping configurations
12
Web beans here may reference "application beans" in the "root" web application context
13
-->
14
15
16
<!-- ################### SPRING MVC VIEW RESOLVER ################### -->
17
<!--
18
IF the Controller returns a logical view name="rewardList" THEN
19
the ViewResolver will return the file "/WEB-INF/jsp/rewardList.jsp"
20
END IF
21
-->
22
<
bean
id
=
"viewResolver"
class
=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
>
23
<
property
name
=
"viewClass"
value
=
"org.springframework.web.servlet.view.JstlView"
></
property
>
24
<
property
name
=
"prefix"
value
=
"/WEB-INF/jsp/"
></
property
>
25
<
property
name
=
"suffix"
value
=
".jsp"
></
property
>
26
<
property
name
=
"order"
><
value
>2</
value
></
property
>
27
</
bean
>
28
29
30
<!-- ################### SPRING MVC CONTROLLER XML ################### -->
31
<
bean
class
=
"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
>
32
<
property
name
=
"mappings"
>
33
<
value
>
34
/loginSecure.do=loginSecureController
35
/myOwnController.do=myOwnController
36
</
value
>
37
</
property
>
38
</
bean
>
39
40
41
<
bean
name
=
"loginSecureController"
class
=
"com.ho.spring3.security.test1.controller.LoginSecureController"
>
42
<
property
name
=
"methodNameResolver"
><
ref
bean
=
"paramResolver"
/></
property
>
43
</
bean
>
44
45
<
bean
name
=
"myOwnController"
class
=
"com.ho.spring3.security.test1.controller.MyOwnController"
>
46
<
property
name
=
"methodNameResolver"
><
ref
bean
=
"paramResolver"
/></
property
>
47
</
bean
>
48
49
<
bean
id
=
"paramResolver"
class
=
"org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"
>
50
<
property
name
=
"paramName"
><
value
>action</
value
></
property
>
51
</
bean
>
52
53
54
<!-- ################### SPRING JSON RESOLVER ################### -->
55
<
bean
id
=
"jsonResolver"
class
=
"org.springframework.web.servlet.view.BeanNameViewResolver"
>
56
<
property
name
=
"order"
><
value
>1</
value
></
property
>
57
</
bean
>
58
59
60
</
beans
>
- Then, we need a Spring MVC controller MyOwnController to handle the web requests for the pages navigation (handlePage0, handlePage1, handlePage2 methods):
01
package
com.ho.spring3.security.test1.controller;
02
03
import
java.io.IOException;
04
05
import
javax.servlet.ServletException;
06
import
javax.servlet.http.HttpServletRequest;
07
import
javax.servlet.http.HttpServletResponse;
08
09
import
org.apache.commons.logging.Log;
10
import
org.apache.commons.logging.LogFactory;
11
import
org.springframework.web.servlet.ModelAndView;
12
import
org.springframework.web.servlet.mvc.multiaction.MultiActionController;
13
14
public
class
MyOwnController
extends
MultiActionController {
15
// ------------------------------------------------------------------ LOG4J
16
protected
final
Log logger = LogFactory.getLog(
this
.getClass());
17
18
// --------------------------------------------------------- PUBLIC METHODS
19
public
ModelAndView handlePage0(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
20
logActionName(
"handlePage0"
);
21
22
return
new
ModelAndView(
"page0"
);
23
}
24
25
public
ModelAndView handlePage1(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
26
logActionName(
"handlePage1"
);
27
28
return
new
ModelAndView(
"page1"
);
29
}
30
31
public
ModelAndView handlePage2(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
32
logActionName(
"handlePage2"
);
33
34
return
new
ModelAndView(
"page2"
);
35
}
36
37
38
// -------------------------------------------------------- PRIVATE METHODS
39
private
void
logActionName(String actionName) {
40
if
(logger.isDebugEnabled()) {
41
logger.debug(
this
.getClass().getSimpleName() + actionName +
"()"
);
42
}
// end-if
43
}
44
}
- So, we need also a Spring MVC controller LoginSecureController to handle the security requests after that the authentication is successful (handleLogin method) or for the logout request (handleLogout method),
01
package
com.ho.spring3.security.test1.controller;
02
03
import
java.io.IOException;
04
05
import
javax.servlet.ServletException;
06
import
javax.servlet.http.HttpServletRequest;
07
import
javax.servlet.http.HttpServletResponse;
08
09
import
org.springframework.web.servlet.ModelAndView;
10
import
org.springframework.web.servlet.mvc.multiaction.MultiActionController;
11
import
com.ho.spring3.security.test1.util.MyRoleUtil;
12
13
14
15
public
class
LoginSecureController
extends
MultiActionController {
16
17
// -------------------------------------------------------------- PUBLIC FUNCTIONS
18
public
LoginSecureController() { }
19
20
public
ModelAndView handleLogin(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
21
if
(logger.isInfoEnabled()) {
22
logger.info(
"LoginSecureController : user "
+ (
new
MyRoleUtil()).getLoggedUserName() +
" logged in."
);
23
MyRoleUtil roleUtil =
new
MyRoleUtil();
24
logger.info(
"User roles = "
+ roleUtil.getLoggedUserRolesNames());
25
}
// end-if
26
27
return
new
ModelAndView(
"redirect:/myOwnController.do?action=handlePage0"
);
28
}
29
30
public
ModelAndView handleLogout(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
31
// Invaliadtion manuelle de la session utilisateur
32
if
(
null
!=request.getSession()){
33
request.getSession().invalidate();
34
}
35
36
if
(logger.isInfoEnabled()) {
37
logger.info(
"LoginSecureController : user "
+ (
new
MyRoleUtil()).getLoggedUserName() +
" logged out."
);
38
}
// end-if
39
40
return
new
ModelAndView(
"redirect:/index.jsp"
);
41
}
42
}
Spring Security components (provider, token)
We will securize the application with Spring security via a custom login form.
- Add the security access configuration file name applicationContext.xml in the folder Spring3Security\war\WEB-INF:
01
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02
<
beans:beans
xmlns
=
"http://www.springframework.org/schema/security"
03
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
04
xmlns:tx
=
"http://www.springframework.org/schema/tx"
05
xmlns:context
=
"http://www.springframework.org/schema/context"
06
xmlns:beans
=
"http://www.springframework.org/schema/beans"
07
xsi:schemaLocation="
16
">
17
18
<!-- ################### SPRING SECURITY ################### -->
19
<
global-method-security
jsr250-annotations
=
"enabled"
secured-annotations
=
"enabled"
/>
20
21
<
http
pattern
=
"/*.jsp"
security
=
"none"
/>
22
<
http
pattern
=
"/*.jsp.*"
security
=
"none"
/>
23
<
http
pattern
=
"/jsp/**"
security
=
"none"
/>
24
<
http
pattern
=
"/css/**"
security
=
"none"
/>
25
<
http
pattern
=
"/images/**"
security
=
"none"
/>
26
<
http
pattern
=
"/img/**"
security
=
"none"
/>
27
<
http
pattern
=
"/js/**"
security
=
"none"
/>
28
29
<
http
auto-config
=
"true"
>
30
31
<!-- Limitation of simultaneous connections -->
32
<
session-management
session-fixation-protection
=
"none"
>
33
<!-- <concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/> -->
34
<
concurrency-control
max-sessions
=
"1"
/>
35
</
session-management
>
36
37
<!-- Access rule: every user should have the role ROLE_BASIC to view any URLs. The syntax, / ** is called "Ant syntax." -->
38
<!-- The role ROLE_BASIC will be affected after authentication -->
39
<
intercept-url
pattern
=
"/*.do.*"
access
=
"ROLE_BASIC"
/>
40
<
intercept-url
pattern
=
"/.*"
access
=
"ROLE_BASIC"
/>
41
<
intercept-url
pattern
=
"/**"
access
=
"ROLE_BASIC"
/>
42
43
44
<!-- Authentication form in Spring Security -->
45
<
form-login
login-page
=
"/login.jsp"
46
authentication-failure-url
=
"/login.jsp?error=true"
47
default-target-url
=
"/loginSecure.do?action=handleLogin"
/>
48
49
50
<!-- Disconnect: By default, the disconnection URL is j_spring_security_logout. -->
51
<
logout
invalidate-session
=
"true"
52
logout-success-url
=
"/index.jsp"
53
logout-url
=
"/loginSecure.do?action=handleLogout"
/>
54
55
</
http
>
56
57
58
<!-- Components AuthenticationProvider -->
59
<
authentication-manager
>
60
<!-- Spring Security password hashing see http://md5.gromweb.com/ -->
61
<!-- <authentication-provider> -->
62
<!-- <password-encoder hash="md5" /> -->
63
<!-- <user-service id="userDetailsService"> -->
64
<!-- <user name="admin" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_BASIC,ROLE_FULLACCESS_XML" /> -->
65
<!-- <user name="user1" password="24c9e15e52afc47c225b757e7bee1f9d" authorities="ROLE_BASIC,ROLE_FULLACCESS_XML" /> -->
66
<!-- <user name="user2" password="7e58d63b60197ceb55a1c487989a3720" authorities="ROLE_BASIC,ROLE_FULLACCESS_XML" /> -->
67
<!-- </user-service> -->
68
<!-- </authentication-provider> -->
69
70
71
<
authentication-provider
ref
=
"myAuthenticationProvider"
/>
72
</
authentication-manager
>
73
74
75
<
beans:bean
id
=
"myAuthenticationProvider"
class
=
"com.ho.spring3.security.test1.util.MyAuthenticationProvider"
>
76
</
beans:bean
>
77
78
</
beans:beans
>
Some explanations:
- We have used a customized HTTP authentication form via the form-login tag: a cutomized form page named login.jsp to which all users will be redirected;
- If the authentication fails, the user will redirect to the URL /login.jsp?error=true; but if the authentication is successful, the user will redirect to the URL /loginSecure.do?action=handleLogin:
1
<
form-login
login-page
=
"/login.jsp"
2
authentication-failure-url
=
"/login.jsp?error=true"
3
default-target-url
=
"/loginSecure.do?action=handleLogin"
/>
- The syntax used in the acess rules /** is named/called “Ant syntax”;
- All JSP pages, css, images, javascript resources will not be securized:
1
<
http
pattern
=
"/*.jsp"
security
=
"none"
/>
2
<
http
pattern
=
"/*.jsp.*"
security
=
"none"
/>
3
<
http
pattern
=
"/jsp/**"
security
=
"none"
/>
4
<
http
pattern
=
"/css/**"
security
=
"none"
/>
5
<
http
pattern
=
"/images/**"
security
=
"none"
/>
6
<
http
pattern
=
"/img/**"
security
=
"none"
/>
7
<
http
pattern
=
"/js/**"
security
=
"none"
/>
- We have defined 1 access rule: every user should have the role ROLE_BASIC to view the URLs *.do. The role ROLE_BASIC will be affected after authentication:
1
<
intercept-url
pattern
=
"/*.do.*"
access
=
"ROLE_BASIC"
/>
2
<
intercept-url
pattern
=
"/.*"
access
=
"ROLE_BASIC"
/>
3
<
intercept-url
pattern
=
"/**"
access
=
"ROLE_BASIC"
/>
- We have set a limitation of simultaneous connections.
1
<
session-management
session-fixation-protection
=
"none"
>
2
<
concurrency-control
max-sessions
=
"1"
/>
3
</
session-management
>
.. it requires the presence of the listener “org.springframework.security.web.session.HttpSessionEventPublisher” in web.xml. The max-sessions attribute specifies the maximum number of simultaneous connections to the same identifier;
- We have specified a logout URL /loginSecure.do?action=handleLogout which will invalide the user’s session;
1
<
logout
invalidate-session
=
"true"
2
logout-success-url
=
"/index.jsp"
3
logout-url
=
"/loginSecure.do?action=handleLogout"
/>
- Finally, we have defined a custom AuthenticationProvider named myAuthenticationProvider. The components AuthenticationProvider of Spring Security, perform the authentication, that is to say it manages the verification of the identity of the user:
1
<
authentication-manager
>
2
<
authentication-provider
ref
=
"myAuthenticationProvider"
/>
3
</
authentication-manager
>
4
5
<
beans:bean
id
=
"myAuthenticationProvider"
class
=
"com.ho.spring3.security.test1.util.MyAuthenticationProvider"
></
beans:bean
>
- The index page index.jsp will redirect the user to a securized URL /loginSecure.do?action=handleLogin for which the login form will be displayed by the Spring security:
1
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
2
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
3
<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
4
<
c:redirect
url
=
"/loginSecure.do?action=handleLogin"
/>
- The login form login.jsp will be display an authentication form, and error message if the authentication has failed:
01
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
02
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
03
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
04
<
html
>
05
<
head
>
06
<
title
>Login Page</
title
>
07
</
head
>
08
<
body
onload
=
'document.f.j_username.focus();'
>
09
<
h3
>Login with Username and Password - JavaBlog.fr - Custom Page</
h3
>
10
<
c:if
test
=
"${param.error == true}"
>
11
<
font
color
=
"red"
>
12
Authentication was not successful, try again.<
br
/>
13
Cause:
14
<
c:out
value
=
"${SPRING_SECURITY_LAST_EXCEPTION.message}"
/><
br
/>
15
<%-- ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}<
br
/> --%>
16
<%-- ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}<
br
/> --%>
17
</
font
>
18
<
br
/>
19
</
c:if
>
20
<
form
name
=
'f'
action
=
"/Spring3Security/j_spring_security_check"
method
=
'POST'
>
21
<
table
>
22
<
tr
>
23
<
td
>User:</
td
>
24
<
td
><
input
type
=
'text'
name
=
'j_username'
value
=
''
>
25
</
td
>
26
</
tr
>
27
<
tr
>
28
<
td
>Password:</
td
>
29
<
td
><
input
type
=
'password'
name
=
'j_password'
/>
30
</
td
>
31
</
tr
>
32
<
tr
>
33
<
td
colspan
=
'2'
><
input
name
=
"submit"
type
=
"submit"
value
=
"submit"
/>
34
</
td
>
35
</
tr
>
36
<
tr
>
37
<
td
colspan
=
'2'
><
input
name
=
"reset"
type
=
"reset"
/>
38
</
td
>
39
</
tr
>
40
</
table
>
41
</
form
>
42
</
body
>
43
</
html
>
- After a successful authentication, the main page page0.jsp will display the user’s informations:
– fullname, email stored in the custom token
– username, roles from the Spring security,01
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
02
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
03
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
04
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
05
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
06
<%@ page import="com.ho.spring3.security.test1.util.MyRoleUtil" %>
07
08
09
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
10
<
html
xmlns
=
"http://www.w3.org/1999/xhtml"
>
11
<
head
>
12
<
META
http-equiv
=
"Cache-Control"
content
=
"no-cache"
>
13
<
META
http-equiv
=
"Pragma"
content
=
"no-cache"
>
14
<
META
http-equiv
=
"Expires"
content
=
"0"
>
15
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
16
<
title
>Page 0</
title
>
17
</
head
>
18
<
body
>
19
20
<%-- <
h2
>Bienvenue <
sec:authentication
property
=
"principal.username"
/>: --%>
21
<
h2
>Bienvenue <%= new MyRoleUtil().getLoggedUserName()%>:</
h2
>
22
* <%= new MyRoleUtil().getCurrentMyUser().getFullName()%><
br
/>
23
* <%= new MyRoleUtil().getCurrentMyUser().getEmail()%><
br
/>
24
<
br
/>
25
26
<
table
>
27
<
tr
><
td
><
a
href
=
"/Spring3Security/loginSecure.do?action=handleLogout"
>Logout</
a
></
td
></
tr
>
28
<
tr
>
29
<
td
><
a
href
=
"myOwnController.do?action=handlePage0"
>page0</
a
></
td
>
30
<
td
> | </
td
>
31
<
td
><
a
href
=
"myOwnController.do?action=handlePage1"
>page1</
a
></
td
>
32
<
td
> | </
td
>
33
<
td
><
a
href
=
"myOwnController.do?action=handlePage2"
>page2</
a
></
td
>
34
</
tr
>
35
</
table
>
36
37
38
<
br
/>
39
Yours roles:
40
<
sec:authentication
property
=
"authorities"
var
=
"authorities"
/>
41
<
ul
>
42
<
c:forEach
items
=
"${authorities}"
var
=
"authority"
>
43
<
li
>${authority}</
li
>
44
</
c:forEach
>
45
</
ul
>
46
47
<
sec:authorize
ifAnyGranted
=
"ROLE_BASIC,ROLE_FULLACCESS_XML,ROLE_FULLACCESS_CUSTOM"
>
48
<
br
/>Code visible for users who have the roles ROLE_FULLACCESS_XML <
u
>or</
u
> ROLE_FULLACCESS_CUSTOM
49
</
sec:authorize
>
50
51
<
sec:authorize
ifAllGranted
=
"ROLE_FULLACCESS_XML,ROLE_FULLACCESS_CUSTOM"
>
52
<
br
/>Code visible for users who have the roles ROLE_FULLACCESS_XML <
u
>and</
u
> ROLE_FULLACCESS_CUSTOM
53
</
sec:authorize
>
54
55
<
sec:authorize
ifNotGranted
=
"ROLE_FULLACCESS_XML,ROLE_FULLACCESS_CUSTOM"
>
56
<
br
/>Code visible for users who don't have the role ROLE_FULLACCESS_XML, <
u
>and neither</
u
> ROLE_FULLACCESS_CUSTOM
57
</
sec:authorize
>
58
59
<
sec:authorize
ifAnyGranted
=
"ROLE_FULLACCESS_XML"
>
60
<
br
/>Code visible for the users who have the role ROLE_FULLACCESS_XML
61
</
sec:authorize
>
62
63
<
sec:authorize
ifAnyGranted
=
"ROLE_FULLACCESS_CUSTOM"
>
64
<
br
/>Code visible for the users who have the role ROLE_FULLACCESS_CUSTOM
65
</
sec:authorize
>
66
67
<
br
/>
68
</
div
>
69
</
body
>
70
</
html
>
- Several pages page1.jsp and page2.jsp will contain the mock application pages like:
01
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
02
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
03
04
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
05
<
html
>
06
<
head
>
07
<
title
>Page 1</
title
>
08
</
head
>
09
<
body
>
10
11
<
table
>
12
<
tr
><
td
><
a
href
=
"/Spring3Security/loginSecure.do?action=handleLogout"
>Logout</
a
></
td
></
tr
>
13
<
tr
>
14
<
td
><
a
href
=
"myOwnController.do?action=handlePage0"
>page0</
a
></
td
>
15
<
td
> | </
td
>
16
<
td
><
a
href
=
"myOwnController.do?action=handlePage1"
>page1</
a
></
td
>
17
<
td
> | </
td
>
18
<
td
><
a
href
=
"myOwnController.do?action=handlePage2"
>page2</
a
></
td
>
19
</
tr
>
20
</
table
>
21
<
hr
/>
22
<
h3
>PAGE 1 </
h3
>
23
</
body
>
24
</
html
>
- The main class is MyAuthenticationProvider which performs the authentication, i.e., it manages the checking of user’s identity via the authenticate method which gets the filled login and password to check with others systems (LDAP, WS, …). In our case, we check only if
the login == password???. Then, we add the technical role ROLE_BASIC for the AUTHENTICATION ONLY, and the applicative ROLE_FULLACCESS_CUSTOM role. And finally, we have set the specific user’s informations (fullname and email) in an simple POJO MyUser
used in the security and representing the User informations stored in the authentication mechanism:01
package
com.ho.spring3.security.test1.util;
02
03
import
java.util.ArrayList;
04
import
java.util.List;
05
06
import
org.springframework.security.authentication.AuthenticationProvider;
07
import
org.springframework.security.authentication.AuthenticationServiceException;
08
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
09
import
org.springframework.security.core.Authentication;
10
import
org.springframework.security.core.AuthenticationException;
11
import
org.springframework.security.core.GrantedAuthority;
12
import
org.springframework.security.core.authority.GrantedAuthorityImpl;
13
14
15
/**
16
* The "AuthenticationProvider" components perform the authentication, i.e., they manage the checking of user's identity.
17
*
18
* @author Huseyin OZVEREN
19
*
20
*/
21
public
class
MyAuthenticationProvider
implements
AuthenticationProvider {
22
23
24
// ---------------------------------- PUBLIC METHODS
25
@Override
26
public
Authentication authenticate(Authentication authentication)
throws
AuthenticationException{
27
// Login
28
//String login= authentication.getName();
29
String login = (String) authentication.getPrincipal();
30
31
// Password
32
String password = (String) authentication.getCredentials();
33
34
35
// Additional details of current (not yet authenticated) user
36
MyUser myUser =
null
;
37
38
if
(password ==
null
|| login ==
null
|| login.trim().length()==
0
|| password.trim().length()==
0
){
39
throw
new
AuthenticationServiceException(
"Your login/password are empty!!!"
);
40
}
41
42
boolean
isAuth=
false
;
43
try
{
44
45
// .... Call remote service like LDAP or WS
46
// login == password???
47
isAuth = (login.equals(password));
48
49
List<GrantedAuthority> AUTHORITIES =
new
ArrayList<GrantedAuthority>();
50
if
(isAuth){
51
// Technical role : FOR AUTHENTICATION ONLY
52
AUTHORITIES.add(
new
GrantedAuthorityImpl(
"ROLE_BASIC"
));
53
54
// .... Get Applicative Roles from remote services LDAP of WS
55
AUTHORITIES.add(
new
GrantedAuthorityImpl(
"ROLE_FULLACCESS_CUSTOM"
));
56
57
// .... Get User Details from remote services LDAP of WS
58
{
59
// ---- FullName
60
String fullName =
"Huseyin OZVEREN"
;
61
// ---- Email
62
String email =
"contact@javablog.fr"
;
63
//
64
myUser =
new
MyUser(fullName, email);
65
}
66
67
}
else
{
68
throw
new
AuthenticationServiceException(
"Your login attempt was not successful."
);
69
}
70
71
72
//return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AUTHORITIES);
73
return
new
MyAuthenticationToken(authentication.getName(), authentication.getCredentials(), AUTHORITIES, myUser);
74
75
}
catch
(AuthenticationServiceException e){
76
throw
e;
77
}
catch
(Throwable e){
78
throw
new
AuthenticationServiceException(
"An error/exception occurs during the authentication. Please, try again."
, e);
79
//Exception ex = new DisabledException(msg);
80
//Exception ex = new CredentialException(msg);
81
//Exception ex = new CredentialExpiredException(msg);
82
//Exception ex = new BadCredentialsException(msg);
83
//Exception ex = new BadCredentialsException(msg);
84
85
}
86
}
87
88
89
@Override
90
public
boolean
supports(Class<?
extends
Object> authentication){
91
return
UsernamePasswordAuthenticationToken.
class
.isAssignableFrom(authentication)
92
&& authentication.equals(UsernamePasswordAuthenticationToken.
class
);
93
}
94
}
- Simple code for the MyUser class:
01
package
com.ho.spring3.security.test1.util;
02
03
import
java.io.Serializable;
04
05
/**
06
* This class is used in the security and represents the User informations stored in the authentication mechanism.
07
*
08
* @author Huseyin OZVEREN
09
*
10
*/
11
public
class
MyUser
implements
Serializable{
12
13
// ------------------------ PRIVATE ATTRIBUTES
14
private
String fullName =
null
;
15
private
String email =
null
;
16
17
18
// ------------------------ CONSTRUCTOR
19
public
MyUser(String fullName, String email){
20
this
.fullName = fullName;
21
this
.email = email;
22
}
23
24
// ------------------------ GET/SET TERS
25
public
String getFullName() {
26
return
fullName;
27
}
28
29
30
public
void
setFullName(String fullName) {
31
this
.fullName = fullName;
32
}
33
34
public
String getEmail() {
35
return
email;
36
}
37
38
public
void
setEmail(String email) {
39
this
.email = email;
40
}
41
}
- Last, we have override the Spring token UsernamePasswordAuthenticationToken by our token MyAuthenticationToken used in the “AuthenticationProvider” component MyAuthenticationProvider:
1
return
new
MyAuthenticationToken(authentication.getName(), authentication.getCredentials(), AUTHORITIES, myUser);
… so, here, the source of our new token MyAuthenticationToken which extends the standard Spring class “UsernamePasswordAuthenticationToken” to allow the storing of additional data/details attached to authenticated user (for example, fullName, email..):
01
package
com.ho.spring3.security.test1.util;
02
03
import
java.util.Collection;
04
05
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
06
import
org.springframework.security.core.GrantedAuthority;
07
08
/**
09
* This component extends the standard Spring class "UsernamePasswordAuthenticationToken" to allow the storing of additional data/details
10
* attached to authenticated user (for example, fullName, email..).
11
*
12
* @author Huseyin OZVEREN
13
*/
14
public
class
MyAuthenticationToken
extends
UsernamePasswordAuthenticationToken{
15
16
// ----------------------------------- PRIVATE ATTRIBUTES
17
private
MyUser myUser =
null
;
18
19
// ----------------------------------- CONSTRUCTOR
20
public
MyAuthenticationToken(Object principal, Object credentials, Collection<?
extends
GrantedAuthority> authorities, MyUser myUser){
21
super
(principal, credentials, authorities);
22
this
.myUser = myUser;
23
}
24
25
// ----------------------------------- GET/SET TERS
26
public
MyUser getMyUser() {
27
return
myUser;
28
}
29
30
public
void
setMyUser(MyUser myUser) {
31
this
.myUser = myUser;
32
}
33
34
}
- We have created a utility class MyRoleUtil using SPRING to retrieve the properties and roles of a authenticated user from the Spring Security Context:
01
package
com.ho.spring3.security.test1.util;
02
03
import
java.util.ArrayList;
04
import
java.util.Collection;
05
06
import
org.springframework.security.core.Authentication;
07
import
org.springframework.security.core.GrantedAuthority;
08
import
org.springframework.security.core.context.SecurityContext;
09
import
org.springframework.security.core.context.SecurityContextHolder;
10
11
/**
12
* <p>
13
* Utility class using SPRING to retrieve the properties and roles of a authenticated user.
14
* </p>
15
*
16
* @author Huseyin OZVEREN
17
*
18
*/
19
public
class
MyRoleUtil {
20
21
// --------------------------------------------------------------------------------------------------- FONCTIONS PUBLIQUES
22
23
/**
24
* return true if at least one role match
25
*/
26
public
boolean
loggedUserHasRole(String ... roles) {
27
SecurityContext context = SecurityContextHolder.getContext();
28
//GrantedAuthority[] authorities = context.getAuthentication().getAuthorities();
29
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) context.getAuthentication().getAuthorities();
30
if
(authorities !=
null
&& authorities.size()>
0
) {
31
for
(GrantedAuthority authority : authorities) {
32
for
(String role : roles) {
33
if
(authority.getAuthority().matches(role)) {
34
return
true
;
35
}
36
}
37
}
38
}
39
return
false
;
40
}
41
42
43
/**
44
* Return the username/login of the user
45
*/
46
public
String getLoggedUserName() {
47
SecurityContext context = SecurityContextHolder.getContext();
48
Authentication authentication = context.getAuthentication();
49
50
String userName =
null
;
51
if
(authentication.getPrincipal()
instanceof
org.springframework.security.core.userdetails.User){
52
org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) authentication.getPrincipal();
53
userName = user.getUsername();
54
}
else
{
55
userName = (String) authentication.getPrincipal();
56
authentication.getName();
57
authentication.getPrincipal();
58
authentication.getCredentials();
59
authentication.isAuthenticated();
60
authentication.getDetails();
61
}
62
return
userName;
63
}
64
65
/**
66
* Return the list of user roles
67
*/
68
public
Collection<String> getLoggedUserRolesNames() {
69
SecurityContext context = SecurityContextHolder.getContext();
70
//GrantedAuthority[] authorities = context.getAuthentication().getAuthorities();
71
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) context.getAuthentication().getAuthorities();
72
Collection<String> roles =
new
ArrayList<String>();
73
if
(authorities !=
null
&& authorities.size()>
0
) {
74
for
(GrantedAuthority authority : authorities) {
75
roles.add(authority.getAuthority());
76
}
77
}
78
return
roles;
79
}
80
81
82
/**
83
* Return the informations/details relatives to the authenticated user
84
* @return
85
*/
86
public
MyUser getCurrentMyUser(){
87
SecurityContext context = SecurityContextHolder.getContext();
88
Authentication authentication = context.getAuthentication();
89
MyUser user =
null
;
90
if
(authentication
instanceof
MyAuthenticationToken){
91
user = ((MyAuthenticationToken)authentication).getMyUser();
92
}
93
94
return
user;
95
}
96
}
Demo and Tests
1. Access URL “http://localhost:8080/Spring3Security/index.jsp“, the index page will redirect to the securized page “/loginSecure.do?action=handleLogin”. So Spring will redirect your request to the custom login form:
URL : http://localhost:8080/Spring3Security/login.jsp
2. If username/password is wrong (login != password), authentication failed, display custom error messages:
URL : http://localhost:8080/Spring3Security/login.jsp?error=true
3. If username/password is correct (login == password), authentication success, display requested page. The welcome page contains several informations concerning the authenticated user like roles from Spring Security context, or user’s personnal informations (in our example, fullname, email) from the overridden token. These datas are accessible via our utility class MyRoleUtil.
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage0
4. If you click on the page1 link:
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage1
5. If you click on the page2 link:
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage2
6. If you click on the logout link (http://localhost:8080/Spring3Security/loginSecure.do?action=handleLogout) which will be redirected to the login page:
URL : http://localhost:8080/Spring3Security/login.jsp
Advanced Demo and Tests: concurrency access
1. Open a first window and access URL “http://localhost:8080/Spring3Security/index.jsp“, the index page will redirect to the securized page “/loginSecure.do?action=handleLogin”. So Spring will redirect your request to the custom login form:
URL : http://localhost:8080/Spring3Security/login.jsp
2. IN WINDOW n°1: Fill in correct username/password (login == password == mylogin), authentication success, display requested page (welcome page):
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage0
3. Open a second window with New Session and access URL “http://localhost:8080/Spring3Security/index.jsp“, the index page will redirect to the securized page “/loginSecure.do?action=handleLogin”. So Spring will redirect your request to the custom login form:
URL : http://localhost:8080/Spring3Security/login.jsp
4. IN WINDOW n°2: Fill in correct username/password (login == password == mylogin), authentication success, display requested page (welcome page):
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage0
5. IN WINDOW n°2: If you click on the page1 link:
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage1
6. IN WINDOW n°1: If you click on the page1 link, SPRING will block your action to an error page with the message “This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).”, because we have set a limitation of simultaneous connections with the same identifier to “1”.
URL : http://localhost:8080/Spring3Security/myOwnController.do?action=handlePage1
Source: Spring3Security
That’s all!!!
Best regards,
Huseyin OZVEREN
the best!!