Cosa è Spring Security?
Spring Security è un framework per la sicurezza delle applicazioni web.
Database
File di creazione della base dati in base al tipo di database
- Oracle
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbuser1', '12345', 'ROLL_USER');</p>
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbadmin1', '12345', 'ROLL_ADMIN');</p>
<p>Commit;
- MySql
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbuser1', '12345', 'ROLL_USER');</p>
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbadmin1', '12345', 'ROLL_ADMIN');
- SqlServer
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbuser1', '12345', 'ROLL_USER');</p>
<p>insert into users (USERNAME, PASSWORD, ROLE)<br>values ('dbadmin1', '12345', 'ROLL_ADMIN');
Creazione Progetto
Dal meù File/New/Other cliccare su “Maven Project“
Selezionare “Create a simple project (skip archetype selection)“
Inserire i seguenti valori:
Group ID: iljavarolo
Artifact ID: SpringMVCSecurityXML
Packaging: war
Cliccare su “Finish“
Il progetto creato dovrebbe avere una struttura del genere:
Se non esiste la cartella WEB-INF crearla.
Configurazione Spring
il progetto completo avrà il seguente aspetto
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>iljavarolo</groupId> <artifactId>SpringMVCSecurityXML</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <!-- Generic properties --> <java.version>1.7</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <repositories> <!-- Repository for ORACLE JDBC Driver --> <repository> <id>codelds</id> <url>https://code.lds.org/nexus/content/groups/main-repo</url> </repository> </repositories> <dependencies> <!-- Spring framework START --> <!-- http://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework/spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- Spring framework END --> <!-- Spring Security Artifacts - START --> <!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-web --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.2.5.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-config --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.2.5.RELEASE</version> </dependency> <!-- Spring Security Artifacts - END --> <!-- Jstl for jsp page --> <!-- http://mvnrepository.com/artifact/javax.servlet/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Servlet API --> <!-- http://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- JSP API --> <!-- http://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!-- MySQL JDBC driver --> <!-- http://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0</version> </dependency> <!-- SQLServer JDBC driver (JTDS) --> <!-- http://mvnrepository.com/artifact/net.sourceforge.jtds/jtds --> <dependency> <groupId>net.sourceforge.jtds</groupId> <artifactId>jtds</artifactId> <version>1.3.1</version> </dependency> </dependencies> <build> <finalName>SpringMVCSecurityXML</finalName> <plugins> <!-- Config: Maven Tomcat Plugin --> <!-- http://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat7-maven-plugin --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <!-- Config: contextPath and Port (Default: /SpringMVCSecurityXML : 8080) --> <!-- <configuration> <path>/</path> <port>8899</port> </configuration> --> </plugin> </plugins> </build> </project>
da notare che nel web.xml si definiscono i due file di configurazione di Spring
- /WEB-INF/spring-security.xml
- /WEB-INF/data-source-cfg.xml
Inoltre, la DispatcherServlet di Spring leggerà il suo file di configurazione in base al principio {servlet-name} ==> /WEB-INF/{servlet-name}-servlet.xml, quindi in questo caso la servlet name vale “spring-mvc” e il file avrà il nome di “spring-mvc-servlet.xml“
mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <context:component-scan base-package="com.iljavarolo" /> <context:annotation-config /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/pages/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>
data-source-cfg.xml per MySQL
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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-4.1.xsd"> <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/prova" /> <property name="username" value="root" /> <property name="password" value="1234" /> </bean> </beans>
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <http use-expressions="true"> <intercept-url pattern="/" access="isAnonymous()" /> <intercept-url pattern="/welcome" access="isAnonymous()" /> <intercept-url pattern="/login" access="isAnonymous()" /> <intercept-url pattern="/logout" access="isAnonymous()" /> <intercept-url pattern="/userInfo" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" /> <intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" /> <intercept-url pattern="/other/**" access="isAuthenticated()" /> <access-denied-handler error-page="/403" /> <form-login login-page='/login' login-processing-url="/j_spring_security_check" default-target-url="/userInfo" always-use-default-target="false" authentication-failure-url="/login?error=true" username-parameter="username" password-parameter="password" /> <logout logout-url="/logout" logout-success-url="/logoutSuccessful" delete-cookies="JSESSIONID" invalidate-session="true" /> </http> <authentication-manager> <authentication-provider> <user-service> <user name="user1" password="12345" authorities="ROLE_USER" /> <user name="admin1" password="12345" authorities="ROLE_USER, ROLE_ADMIN" /> </user-service> </authentication-provider> <!-- authentication from database --> <authentication-provider> <jdbc-user-service data-source-ref="myDataSource" users-by-username-query="select username,password, enabled from users where username=?" authorities-by-username-query="select username, role from users where username=?" /> </authentication-provider> </authentication-manager> </beans:beans>
Da notare che in questo file:
- E’ dichiarata una pagina per l’accesso negato con:
<access-denied-handler error-page=”/403″ /> - Per ogni Url si definisce quale ruolo puo accedere:
<intercept-url pattern=”/admin” access=”hasRole(‘ROLE_ADMIN’)” />
in questo caso l’url /admin può essere visualizzato solo dal ruolo ROLE_ADMIN - Sono dichiarati i dettagli della login page. In particolare:
<form-login login-page=’/login’ login-processing-url=”/j_spring_security_check”
default-target-url=”/userInfo” always-use-default-target=”false”
authentication-failure-url=”/login?error=true” username-parameter=”username”
password-parameter=”password” />
login-page=”/login” – La pagina di login da visualizzare
authentication-failure-url=”/login?error” – Se l’autenticazione fallisce, si viene reindirizzati sulla pagina /login?error
login-processing-url=”/j_spring_security_check”: Specifica l’url a cui il login form (che conterrà ovviamente le credenziali user e password)sarà submittato.
Se si omette o si valorizza con “/j_spring_security_check“, il login form sarà inoltrato al controller di default di Spring.
j_username = la username corretta
j_password = la password corretta
default-target-url = “/userInfo”= landing page in caso di successo
Da notare che i valori messi in login-processing-url, j_username e j_password dovranno essere specificati anche nel form della jsp con quell id
ossia esempio
</ol>
<form action="j_spring_security_check" method="POST" name="f">
<table>
<tbody>
<tr>
<td>User:</td>
<td><input name="username" type="text" value="" /></td>
</tr>
<tr>
<td>Password:</td>
<td><input name="password" type="password" /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</tbody>
</table>
</form>
- Nel tag <user-service> si definiscono i vari utenti con i loro ruoli: <user name=”admin1″ password=”12345″ authorities=”ROLE_USER, ROLE_ADMIN” />
- Nel tag intercept-url si definisce, per ogni ruolo, quali risorse può vedere. Per esempio: <intercept-url pattern=”/userInfo”access=”hasAnyRole(‘ROLE_USER’, ‘ROLE_ADMIN’)” />in questo caso l url /userInfo puo essere vista da questi ruoli ‘ROLE_USER’, ‘ROLE_ADMIN’
- Si indica la query da fare per controllare se le credenziali sono corrette: <authentication-provider><jdbc-user-service data-source-ref=”myDataSource” users-by-username-query=”select username,password, enabled from users where username=?” authorities-by-username-query=”select username, role from users where username=?” /></authentication-provider>
Gli elementi sono self-documenting. Basta passare il mouse su qualsiasi tag e si vedrà una breve descrizione.
Views
403Page.jsp
<%@page session="false"%> <html> <head> <title>${title}</title> </head> <body> <h2>${message}</h2> </body> </html>
adminPage.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page session="true"%> <html> <head><title>${title}</title></head> <body> <h2>Message : ${message}</h2> <c:if test="${pageContext.request.userPrincipal.name != null}"> <h3>Welcome : ${pageContext.request.userPrincipal.name} | <a href="<c:url value="/logout" />" >Logout</a></h3> </c:if> </body> </html>
loginPage.jsp
<html> <head><title>Login</title></head> <body> <h1>Login</h1> <form name='f' action="j_spring_security_check" method='POST'> <table> <tr> <td>User:</td> <td><input type='text' name='username' value=''></td> </tr> <tr> <td>Password:</td> <td><input type='password' name='password' /></td> </tr> <tr> <td><input name="submit" type="submit" value="submit" /></td> </tr> </table> </form> </body> </html>
logoutSuccessfulPage.jsp
<html> <head><title>Logout</title></head> <body> <h1>Logout Successful!</h1> </body> </html>
userInfoPage.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page session="true"%> <html> <head><title>${title}</title></head> <body> <h2>Message : ${message}</h2> <c:if test="${pageContext.request.userPrincipal.name != null}"> <h3>User Info : ${pageContext.request.userPrincipal.name} | <a href="<c:url value="/logout" />" >Logout</a></h3> </c:if> </body> </html>
welcomePage.jsp
<%@page session="false"%> <html> <head> <title>${title}</title> </head> <body> <h1>Message : ${message}</h1> </body> </html>
Classi Java
In questo controller si effettuano i mapping delle richieste.
Esempio
@RequestMapping(value = “/admin”, method = RequestMethod.GET)
public String adminPage(Model model) {
model.addAttribute(“title”, “Admin”);
model.addAttribute(“message”, “Admin Page – This is protected page!”);
return “adminPage“;
}
Quando arriverà la request /admin verrà chiamato il metodo adminPage e la string di ritorno “adminPage” costituisce il nome della jsp da richiamare.
MainController.java
package com.iljavarolo.springmvcsecurity.controller; import java.security.Principal; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class MainController { @RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET) public String welcomePage(Model model) { model.addAttribute("title", "Welcome"); model.addAttribute("message", "This is welcome page!"); return "welcomePage"; } @RequestMapping(value = "/admin", method = RequestMethod.GET) public String adminPage(Model model) { model.addAttribute("title", "Admin"); model.addAttribute("message", "Admin Page - This is protected page!"); return "adminPage"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String loginPage(Model model) { model.addAttribute("title", "Login"); model.addAttribute("message", "Enter your username/password:"); return "loginPage"; } @RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET) public String logoutSuccessfulPage(Model model) { model.addAttribute("title", "Logout"); return "logoutSuccessfulPage"; } @RequestMapping(value = "/userInfo", method = RequestMethod.GET) public String loginPage(Model model, Principal principal) { model.addAttribute("title", "User Info"); // Sau khi user login thanh cong se co principal String userName = principal.getName(); model.addAttribute("message", "User Info - This is protected page!. Hello " + userName); return "userInfoPage"; } @RequestMapping(value = "/403", method = RequestMethod.GET) public String accessDenied(Model model, Principal principal) { model.addAttribute("title", "Access Denied!"); if (principal != null) { model.addAttribute("message", "Hi " + principal.getName() + "<br> You do not have permission to access this page!"); } else { model.addAttribute("msg", "You do not have permission to access this page!"); } return "403Page"; } }
Build Applicazione
Buildare l’intero progetto, cliccando su run As–>Maven Install
Dal menù “Run Configuration” settare la seguente configurazione per l’avvio:
inserire i seguenti valori:
Name: Run SpringMVCSecurityXML
Base directory: ${workspace_loc:/SpringMVCSecurityXML}
Goals: tomcat7:run
Buildare l’intero progetto cliccando su “Run“:
Avvio Applicazione
digitare nel browser questa url:
http://localhost:8080/SpringMVCSecurityXML/userInfo
Se i dati inseriti sono errati, si verra reindirizzato sempre sulla stessa pagina di login.
Inserendo invece le credenziali corrette:
Se si digita questa URL
http://localhost:8080/SpringMVCSecurityXML/admin
si ottiene:
Questo perchè questa risorsa è permessa solo per l’ ADMIN (ROLE-ADMIN).
Se si fa il logout e si ritorna sull’ url su citato si verrà reindirizzati nella normale pagina di login