Spring Security — создание группы ролей (group authorities). Создание таблиц базы данных.
Добавление групп ролей авторизации из базы данных в Spring Security. Подключение базы данных пользователей и их прав доступа к Spring Security.
Используемые технологии
- Spring 4.2.2.RELEASE
- Spring Security 4.0.2.RELEASE
- MySQL 5.6.25
- JSP 1.2.1
- IntelliJ IDEA 14
- Maven 3.2.5
1. Описание задачи
Добавить связь приложения с Spring Security получать данные о пользователях, их правах и паролях из базы данных MySQL. Создание групп ролей аутентификации для упрощения смены прав доступа в приложении большому числу пользователей.
2. Создание базы данных MySQL
Основа приложения взята из Spring Security – пример приложения Hello World. Базовые настройки. Spring Security example. Изменения в представлениях и настройках будут отображены по ходу статьи. Структура проекта не изменилась.
Если вы не знаете как подключить созданную базу данных MySQL к IntelliJ IDEA, то рекомендую к прочтению создание MySQL базы данных и подключение к IntelliJ IDEA.
Согласно документации, для настроек по умолчанию, можно настроить базу данных в двух видах:
- Таблица с пользователями и таблица с правами
- К первому варианту добавляется таблица с группами пользователей (админы, гости и т.п.), таблица с правами для каждой группы (админы могут всё, пользователи чуть меньше и т.п.), а так же таблица с членами каждой группы (Вася входит в админы, Петя входит в пользователи и т.п.).
Мы будем использовать второй вариант. Если вам достаточно только первого варианта, то поменять код из статьи будет не сложно.
Код создания таблиц и записей для MySQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
CREATE DATABASE IF NOT EXISTS `users` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `users`; /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `group_authorities` -- DROP TABLE IF EXISTS `group_authorities`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `group_authorities` ( `group_id` bigint(20) NOT NULL, `authority` varchar(50) NOT NULL, KEY `fk_group_authorities_group` (`group_id`), CONSTRAINT `fk_group_authorities_group` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `group_authorities` -- LOCK TABLES `group_authorities` WRITE; /*!40000 ALTER TABLE `group_authorities` DISABLE KEYS */; INSERT INTO `group_authorities` VALUES (1,'ROLE_ADMIN'),(1,'ROLE_USER'),(2,'ROLE_USER'),(1,'ROLE_GUEST'),(2,'ROLE_GUEST'),(3,'ROLE_GUEST'); /*!40000 ALTER TABLE `group_authorities` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `group_members` -- DROP TABLE IF EXISTS `group_members`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `group_members` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `group_id` bigint(20) NOT NULL, PRIMARY KEY (`id`), KEY `fk_group_members_group` (`group_id`), CONSTRAINT `fk_group_members_group` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- -- Dumping data for table `group_members` -- LOCK TABLES `group_members` WRITE; /*!40000 ALTER TABLE `group_members` DISABLE KEYS */; INSERT INTO `group_members` VALUES (1,'admin',1),(2,'chief',1),(3,'user',2),(4,'guest',3); /*!40000 ALTER TABLE `group_members` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `groups` -- DROP TABLE IF EXISTS `groups`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `groups` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `group_name` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `groups` -- LOCK TABLES `groups` WRITE; /*!40000 ALTER TABLE `groups` DISABLE KEYS */; INSERT INTO `groups` VALUES (1,'admins'),(2,'users'),(3,'guests'); /*!40000 ALTER TABLE `groups` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `users` -- DROP TABLE IF EXISTS `users`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `users` ( `username` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, `enabled` tinyint(1) NOT NULL, PRIMARY KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `users` -- LOCK TABLES `users` WRITE; /*!40000 ALTER TABLE `users` DISABLE KEYS */; INSERT INTO `users` VALUES ('admin','admin',1),('chief','chief',1),('guest','guest',1),('user','user',1); /*!40000 ALTER TABLE `users` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; |
Если вставить этот код в MySQL WorkBench, то вы получите как созданную схему с таблицами, так и тестовые данные внутри неё.
3. Настройка конфигурации spring-config*.xml
spring-security.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <http realm="JavaStudy example" use-expressions="true"> <intercept-url pattern="/" access="isFullyAuthenticated() or isAnonymous()"/> <intercept-url pattern="/login.jsp*" access="isFullyAuthenticated() or isAnonymous()"/> <intercept-url pattern="/admin" access="hasRole('ADMIN')"/> <intercept-url pattern="/exitUser*" access="isFullyAuthenticated() or isAnonymous()"/> <intercept-url pattern="/**" access="hasRole('USER')"/> <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/> <access-denied-handler error-page="/accessDenied.jsp"/> <http-basic/> <logout logout-success-url="/exitUser.jsp"/> <remember-me /> <headers/> <csrf/> </http> <authentication-manager> <authentication-provider user-service-ref="jdbcGroupsImpl"/> </authentication-manager> <b:bean id="jdbcGroupsImpl" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> <b:property name="enableGroups" value="true"/> <b:property name="enableAuthorities" value="false"/> <b:property name="dataSource" ref="dataSource"/> </b:bean> </b:beans> |
Для темы статьи важны два последних тега <authentication-provider user-service-ref=»jdbcGroupsImpl»/> и <b:bean id=»jdbcGroupsImpl». Т.к. мы используем вариант с группами ролей, то в свойствах указываем enableGroups=true и enableAuthorities=false. Последнее свойство указывает на источник базы данных dataSource:
data-source.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/users"/> <property name="username" value="root"/> <property name="password" value="admin"/> </bean> </beans> |
Здесь указан драйвер подключения к базе данных, путь к ней, а так же логин-пароль для доступа к схеме.
Остальные настройки доступны по клику:
dispatcher-servlet.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Dispatcher Servlet - определяет инфраструктуру обработки запросов сервлетом --> <!-- Включает поддержку @Controller и др. в Spring MVC --> <mvc:annotation-driven/> <!-- Обрабатывает HTTP GET запросы для указанного каталога более эффективно для статических ресурсов --> <mvc:resources mapping="/resources/**" location="resources"/> <!-- Определяет какое представление рендерить на основании вычеслений в @Controller (return ModelAndView) --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- Сканирует пакет для поиска бинов --> <context:component-scan base-package="ru.javastudy"/> <!-- Включает Spring аннотации вроде @Autowired--> <context:annotation-config/> </beans> |
web.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/applicationContext.xml /WEB-INF/spring/spring-security-config.xml WEB-INF/spring/datasource-config.xml </param-value> </context-param> <!--Loads the root application context of this web app at startup. The application context is then available via WebApplicationContextUtils.getWebApplicationContext(servletContext). --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Provides core MVC application controller. See contacts-servlet.xml. --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/spring/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <!-- Need to enable xml config 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> <!-- --> <!-- page on load--> <welcome-file-list> <welcome-file>redirectPage.jsp</welcome-file> </welcome-file-list> </web-app> |
pom.xml (добавлен mysql-connector-java):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
<?xml version="1.0" encoding="UTF-8"?> <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>ru.javastudy</groupId> <artifactId>springSecurity</artifactId> <version>1.0</version> <properties> <spring-framework-version>4.2.2.RELEASE</spring-framework-version> <spring-security-version>4.0.2.RELEASE</spring-security-version> <slf4j-version>1.7.12</slf4j-version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring-framework-version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Spring security minimal dependency--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring-security-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring-security-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security-version}</version> <scope>runtime</scope> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <!--ExpressionLanguage, Servlet 3.0, JPA 2, EJB, CDI, etc--> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <!--<scope>provided</scope>--> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Logger. Also need to start Tomcat 8+ --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j-version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j-version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j-version}</version> <scope>runtime</scope> </dependency> <!-- MySQL connection driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency> </dependencies> </project> |
4. Описание представлений
В целом вся логика осталась из статьи, указанной в начале, но были добавлены незначительные изменения для удобства вывода и навигации. Посмотреть все страницы jsp и изменения можно скачав исходные коды в конце статьи.
login.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page pageEncoding="UTF-8" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Title Login Page</title> </head> <body> <h1>Login page</h1> <p>Valid users: <p>username: <b>user</b>, password: <b>user</b></p> <p>username: <b>admin</b>, password: <b>admin</b></p> <p>username: <b>guest</b>, password: <b>guest</b></p> <c:if test="${not empty param.login_error}"> <span style="color: red; "> Your login attempt was not successful, try again.<br/><br/> Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> </span> </c:if> <form name="frm" action="<c:url value='login'/>" 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"></td></tr> <tr><td colspan="2"><input name="reset" type="reset"></td></tr> </table> <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> </form> </body> </html> |
welcomePage.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<%@ include file="/WEB-INF/views/include.jsp" %> <html> <head><title>Welcome page</title></head> <body> <p>This is welcome file page (webapp\WEB-INF\views\welcomePage.jsp). <p>Click button to try security! <p>Click me to go inside app! (link handle by MainController) <a href="<c:url value="/second"/>">Go!</a> <br> <p>Next link for admin only! Try it</p> <a href="<c:url value="/admin"/>">AdminPage</a> </body> </html> |
adminPage.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<%@ include file="/WEB-INF/views/include.jsp" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title></title> </head> <body> This is admin page <br> Hello ${pageContext.request.userPrincipal.name} <security:authorize access="isRememberMe()"> <p><h3> ${pageContext.request.userPrincipal.name} login with "remember me" cookies</h3> </security:authorize> <security:authorize access="isFullyAuthenticated()"> <p><h3> ${pageContext.request.userPrincipal.name} login with username and password</h3> </security:authorize> <form action="<c:url value="/logout"/>" method="post"> <input type="submit" value="Logoff"/> (also clears any remember-me cookie) <security:csrfInput/> </form> </body> </html> |
accessDenied.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<%@ include file="/WEB-INF/views/include.jsp" %> <html> <head> <title>Access Denied</title> </head> <body> <h1>Sorry, access is denied</h1> <table> <tr><td>Current User:</td><td> <% Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { %> <%= auth.getPrincipal().toString() %> <% } %> </td></tr> </table> <form action="<c:url value="/logout"/>" method="post"> <input type="submit" value="Logoff"/> (also clears any remember-me cookie) <security:csrfInput/> </form> </body> </html> |
exitUser.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<%@ include file="/WEB-INF/views/include.jsp" %> <%@ page pageEncoding="UTF-8" %> <head> <title>Exit User</title> </head> <h1>Exit User</h1> <c:if test="${not empty param.login_error}"> <span style="color: red; "> Your 'Exit User' attempt was not successful, try again.<br/><br/> Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> </span> </c:if> <table> <tr><td>Current User:</td><td> <% Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { %> <%= auth.getPrincipal().toString() %> <% } %> </td></tr> </table> <a href="<c:url value="/login.jsp"/>">Login</a> </body> </html> |
Исходные коды
Spring Security DataBaseConnection — исходные коды. Дамп для MySQL внутри.
12One thought on “Spring Security — создание группы ролей (group authorities). Создание таблиц базы данных.”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
а каков будет вариант для одной таблицы?
все дело в том, что хочу сделать приложение с одной таблицей в которой будут следующие поля:
user_role (для ограничений)
user_name (для вывода на страницах)
user_login (для входа)
user_pass (для входа)
соответственно, таблица будет неизменяемой, и в ней будет только три пользователя с разными правами, типа админ, оператор и наблюдатель
подскажите как поступить в этом случае,
спасибо
и еще пару вопросов:
1. можно ли дать доступ сразу к группе страниц с одинаковым префиксом в имени к примеру
2. возможность скрывать часть страницы
к примеру: страница одна, но пользователь видит 2 кнопки, а админ видит 3
спасибо