Spring MVC и Spring Security. Пример настройки страницы логина, настройка ролей
Spring MVC и Spring Security. Настройка Spring Security, создание страницы логина, настройка ролей, ограничение доступа к частям приложения с помощью аннотаций или xml настроек. Обзор базовых security тегов на странице jsp.
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
Используемые технологии и библиотеки
- Spring MVC 4.2.4.Release
- Spring Security 4.0.4.Release
- IntelliJ IDEA 15.0.2
1. Описание задачи
Подключить модуль Spring Security 4 к нашему приложению Spring MVC. Настроить начальную страницу login, добавить страницы для отображения информации о нарушении прав доступа. Создать конфигурационный файл настроек Spring Security. Настроить роли, проверку авторизации пользователя с помощью информации из базы данных, а так же добавить некоторые дополнительные возможности из Spring Security.
Возможно вам будут интересны статьи из раздела Spring Security.
2. Структура проекта
В проекте используется один Java класс-контроллер (SecurityController), с помощью которого обрабатываются запросы пользователя. Созданы несколько jsp представлений (login, admin, profile, security) с разными уровнями доступа. В случае недостаточных прав, пользователь будет перенаправлен на одну из страниц ошибки (пакет error).
3. pom.xml
Для использования возможностей Spring Security нам необходимо подключить несколько зависимостей этого модуля в проект.
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 |
<!-- Spring Security --> <spring-security.version>4.0.4.RELEASE</spring-security.version> ...... ...... <!--Spring Security--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring-security.version}</version> </dependency> <!--To start using the security namespace in your application context--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring-security.version}</version> </dependency> <!--Provides Spring Security’s JSP tag implementations.--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <!--Common Java Annotations. For this app it includes security annotation: @RolesAllowed; @DenyAll, @RunAs, @PermitAll --> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency> |
Первые три зависимости относятся напрямую к модулю Spring Security. С помощью подключения этих зависимостей вы получаете возможность использования проверки безопасности в веб проекте, использовать пространство имен spring security и jsp тэгов прямо внутри страницы представления. Так же в проект была добавлена зависимость спецификации jsr-250. Она была добавлена для использования в контроллере аннотации @RolesAllowed и сравнении с аннотациями из Spring.
Полный файл pom.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 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
<?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>mvc_html5_angular</artifactId> <version>1.0</version> <properties> <!-- Generic properties --> <java.version>1.8</java.version> <!-- Web --> <jsp.version>2.2</jsp.version> <!--Java standard tag library--> <jstl.version>1.2</jstl.version> <servlet.version>3.1.0</servlet.version> <!-- Spring --> <spring-framework.version>4.2.4.RELEASE</spring-framework.version> <!-- JUnit test --> <junit.version>4.12</junit.version> <!-- Logging --> <!--logback - improved version of log4j--> <logback.version>1.0.13</logback.version> <slf4j.version>1.7.13</slf4j.version> <!-- jackson json JSON Processing API --> <jackson.databind-version>2.2.3</jackson.databind-version> <!-- Hibernate / JPA --> <hibernate.version>5.0.1.Final</hibernate.version> <!-- I don't know why, but with 5.0.5 final app not working! --> <!-- Spring Data --> <spring-framework.data.version>1.9.1.RELEASE</spring-framework.data.version> <!-- Quartz scheduling framework --> <quartz.scheduling.version>2.2.1</quartz.scheduling.version> <!-- Spring Security --> <spring-security.version>4.0.4.RELEASE</spring-security.version> </properties> <dependencyManagement> <!--all spring dependencies --> <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> <!--bootstrap webjars.org--> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <!-- Other Servlet Web dependencies --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <!--Servlet API--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>${jsp.version}</version> <scope>provided</scope> </dependency> <!-- Apache Commons File Upload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <!-- Excel view --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.12</version> </dependency> <!-- PDF view --> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.5</version> </dependency> <!-- HSQLDB embedded database. Встроенная база данных--> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.3</version> </dependency> <!-- Spring JDBC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <!--JUnit Test--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Test Artifacts with Spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <!-- Logging with SLF4J & LogBack --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> <scope>runtime</scope> </dependency> <!--Contains org.springframework.mail.javamail--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring-framework.version}</version> </dependency> <!-- Spring MVC Mail Related Dependency --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> <!-- Spring REST jackson JSON Processing API --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.databind-version}</version> </dependency> <!--Hibernate ORM--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <!--Hibernate validator (contains @NotEmpty)--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.0.Final</version> </dependency> <!--Spring Data--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring-framework.data.version}</version> </dependency> <!-- Quartz scheduling framework --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>${quartz.scheduling.version}</version> </dependency> <!--Spring Security--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring-security.version}</version> </dependency> <!--To start using the security namespace in your application context--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring-security.version}</version> </dependency> <!--Provides Spring Security’s JSP tag implementations.--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <!--Common Java Annotations. For this app it includes security annotation: @RolesAllowed; @DenyAll, @RunAs, @PermitAll --> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> </plugins> <!--need to find configs in tests in package web-inf like @ContextConfiguration(locations = {"classpath:/config/application-context.xml" --> <testResources> <testResource> <directory>src/main/webapp/WEB-INF/config</directory> </testResource> </testResources> </build> </project> |
4. Файл конфигурации Spring Security
После добавления зависимостей для Spring Security нам необходимо настроить конфигурационный файл приложения. Принято выносить настройки безопасности в отдельный xml файл (или класс, если используете аннотации). Был создан файл security-context.xml рядом с остальными файлами конфигурации спринг. Это можно было сделать автоматически нажав правой кнопкой на проект > Add Framework Support.. и выбрав там Spring Security (возможно у вас был добавлен весь проект Spring и тогда выбор будет не доступен). В любой случае всё что нужно это создать этот xml файл и проверить, что он добавлен в контекст нашего приложения.
Перед тем как перейти к настройкам безопасности, в mvc-config.xml была добавлена одна строчка (а так же пространство имен spring-security.xsd) для того, чтобы заработали аннотации Spring Security в Java классах.
mvc-config.xml:
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "http://www.springframework.org/schema/beans" ..... xmlns:security= "http://www.springframework.org/schema/security" xsi:schemaLocation= "http://www.springframework.org/schema/beans ..... http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!--Spring security enabled annotations--> <security:global-method-security pre-post-annotations= "enabled" secured-annotations= "enabled" jsr250-annotations= "enabled"/> |
- security: — пространство имен Spring Security, которое задается в начале файла.
- global-method-security — предоставляет методы безопасности для всех бинов, зарегистрированных в контексте Spring приложения. В частности, бины будут проверяться на наличие совпадений с упорядоченным списком дочерних элементов protect-pointcut и\или аннотаций Spring Security. Подробнее в документации или в справке IDEA (ctrl+q).
- secured-annotations, jsr250-annotations — включение аннотаций из соответствующих библиотек.
Наконец перейдем к главному файлу настроек безопасности Spring Security для нашего приложения.
security-config.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 |
<?xml version= "1.0" encoding= "UTF-8"?> <beans:beans xmlns:beans= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns= "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"> <!-- auto-config= "true" > Automatically registers a login form, BASIC authentication, logout services. use-expressions Enables EL-expressions in the access attribute --> <http auto-config= "true" use-expressions= "true" > <!--used to define the set of URL patterns that the application is interested in and to configure how they should be handled. It is used to construct the FilterInvocationSecurityMetadataSource used by the FilterSecurityInterceptor--> <intercept-url pattern= "/security/**" access= "hasRole('ADMIN')"/> <!--Если попытаться войти без логина на страницу в этом маппинге (например http://localhost:8080/user/), то будет переброшено на страницу, указанную в form-login login-page='pageName.html' --> <intercept-url pattern= "/user/**" access= "hasRole('USER')"/> <form-login login-page= "/login.html" username-parameter= "j_username" password-parameter= "j_password" login-processing-url= "/j_spring_security_check" authentication-failure-url= "/login.html?error=true" /> <logout logout-url= "/j_spring_security_logout" logout-success-url= "/"/> <!--Save logged user in cookie with name key='name' --> <remember-me key= "myKey" token-validity-seconds= "300"/> <csrf disabled= "true"/> </http> <jdbc-user-service id= "jdbcUserService" data-source-ref= "dataSource" users-by-username-query= "SELECT USERNAME, PASSWORD, ENABLED FROM USER WHERE USERNAME=?" authorities-by-username-query= "SELECT U.USERNAME, A.AUTHORITY FROM AUTHORITIES A, USER U WHERE U.USERNAME = A.USERNAME AND U.USERNAME = ? "/> <authentication-manager alias= "authenticationManager"> <authentication-provider user-service-ref= "jdbcUserService"/> </authentication-manager> </beans:beans> |
После определения пространства имен в тэге <http> </http> указываются атрибуты auto-config и use-expressions. Первый позволяет создать форму авторизации по умолчанию, второй атрибут позволяет использовать выражения (например hasRole(‘ADMIN’)).
<intercept-url> задает паттерн по которому будет проверяться уровень доступа клиента. Например <intercept-url pattern= «/user/**» access= «hasRole(‘USER’)»/> будет показывать страницу по адресу URL, содержащему /user/**, только пользователям с ролью USER. Пример: если попытаться войти без логина на страницу http://localhost:8080/user/,
то клиент будет переброшен на страницу, указанную в form-login login-page=’pageName.html’.
- <form-login> задает различные настройки страницы авторизации (формы).
- login-page — адрес страницы ввода логина и пароля. Если здесь ничего не указать, то Spring Security сгенерирует эту страницу самостоятельно.
- username-parameter и password-parameter — параметры, которые используются для связывания с именем пользователя и паролем, которые были введены пользователем в форму. На странице login.jsp это выглядит так:
1 2 3 4 5 |
<label for= "inputEmail" class= "sr-only"><spring:message code= "email" text= "Email"/></label> <input id= "inputEmail" class= "form-control" name= "j_username" value= "admin@gmail.com" required autofocus/> <label for= "inputPassword" class= "sr-only"><spring:message code= "pass" text= "Password"/></label> <input type= "password" id= "inputPassword" class= "form-control" name= "j_password" value= "12345" required/> |
- login-processing-url — задает значение action у form при котором Spring Security понимает, что нужно проверять пользователя согласно настройкам.
- authentication-failure-url — URL, который будет сгенерирован в случае ошибки. Заметьте, что здесь мы передаем в качестве параметра с именем error значение true. Этот параметр проверяется на странице login.jsp.
1 2 3 |
<c:if test= "${not empty param.error}"> <font size= "2" color= "red"><b>Неправильный логин или пароль</b></font> </c:if> |
- <logout logout-url= ..> — параметр для выхода. Значение в этом атрибуте далее используется на странице представления (на кнопке выход). Например на странице admin.jsp:
1 2 3 4 5 |
<p>Ссылка logout имеет атрибут <span style= "color: #0080c0;">/j_spring_security_logout</span>, который прописан в security-config.xml </p> <span style= "color: #568C00;"><security:authentication property= "principal.username"/></span> <a style= "color: red;" href= "<c:url value= "/j_spring_security_logout"/>">Logout</a> |
- remember-me — создает чекбокс «запомни меня». Далее указано время жизни. Запоминание происходит путем использования cookie с именем, указанным в key (у нас — myKey).
- csrf — защита от этого типа атак. Подробнее в интернете.
Далее идет настройка проверки пользователей с помощью запроса к базе данных. Мы используем источник данных ‘dataSource‘, который задан в application-context.xml еще в части, где рассматривалась работа с БД (JDBC). Затем мы записываем в прямую запрос SQL, который будет выполнен для проверки пользователя. authentication-manager и authentication-provider можно настроить различными способами, здесь просто указывается алиас и сервис для проверки аутентификации (jdbc-user-service, описанный чуть выше).
5. Controller
В приложение написан один небольшой контроллер, который будет обрабатывать запросы с security.jsp. SecurityController:
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 |
package ru.javastudy.mvcHtml5Angular.mvc.security; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import javax.annotation.security.RolesAllowed; /** * Created for JavaStudy.ru on 04.03.2016. */ @Controller public class SecurityController { //JSR-250 Security @RolesAllowed(value={"ROLE_SUPER_USER", "ROLE_ADMIN"}) @RequestMapping(value = "/adminOrSuperUserCanCall", method = RequestMethod.GET) public ModelAndView adminOrSuperUserCanCall() { System.out.println("SecurityController adminOrSuperUserCanCall() is called"); return new ModelAndView("/security/admin"); } //Spring Security //SpEL usage at method level security @PreAuthorize("hasRole('ADMIN') || hasRole('SUPER_USER') || hasRole('USER')") @RequestMapping(value= "/userOrAdminCanCallSpEL", method=RequestMethod.GET) public ModelAndView userOrAdminCanCall() { System.out.println("SecurityController userOrAdminCanCall() is called with ROLE_ADMIN or ROLE_USER"); return new ModelAndView("/security/profile"); } //Spring Security @Secured(value={"ROLE_ADMIN"}) @RequestMapping(value= "/adminMethodSecured", method=RequestMethod.GET) public ModelAndView adminMethodSecured() { System.out.println("SecurityController adminMethodSecured() is called with ADMIN ROLE"); return new ModelAndView("/security/admin"); } } |
@RolesAllowed — аннотация из стандарта JSR-250. Если пользователь обладает указанной ролью, то метод будет выполнен.
@PreAuthorize — аналогичная по назначению аннотация из Spring Security, в которой можно использовать выражения.
@Secured — еще один вариант проверки пользователя на обладание роли.
В случае попытки вызова этих методов пользователем без необходимых прав будет выброшено исключение org.springframework.security.access.AccessDeniedException: Access is denied.
6. Представления jsp
Для этого раздела было подготовлено несколько страниц jsp, в которых демонстрируются различные возможности Spring Security.
6.1. Header (менюшка сверху)
В header меню была добавлена проверка на авторизацию пользователя и ссылка на страницу авторизации. Напоминаю, что менюшка сверху прописана в файле-шаблоне template.tag.
Код выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<security:authorize access= "hasAnyRole('ROLE_ADMIN','ROLE_SUPER_USER', 'ROLE_USER')" var= "isUSer"/> <c:if test= "${not isUSer}"> <li style= "padding-top: 15px; padding-bottom: 15px; color: red"> <c:if test= "${empty param.error}"> Вы не вошли в приложение </c:if> </li> <li> <a style= "color: Green;" href= "<c:url value= "/login.html"/>">Login</a> </li> </c:if> <c:if test= "${isUSer}"> <li style= "padding-top: 15px; padding-bottom: 15px; color: green"> Вы вошли как: <security:authentication property= "principal.username"/> с ролью: <b><security:authentication property= "principal.authorities"/></b> </li> <li> <a style= "color: red;" href= "<c:url value= "/j_spring_security_logout"/>">Logout</a> </li> </c:if> |
Вначале мы проверяем имеет ли пользователь одну из указанных ролей. Если нет (а так же параметр error пустой; напомню он прописывался в файле настроек security-config.xml и далее используется на нескольких jsp страницах), то выводится сообщение «Вы не вошли в приложение«. В противном случае используется запись ‘principal.username’, которая возвращает имя пользователя.
6.2 login.jsp (страница авторизации)
Если мы нажмем «Войти» (‘Login’) или попробуем зайти на страницу, где требуется определенная роль, то мы попадем на страницу авторизации login.jsp. Напомню, что она указывается в spring-config.xml как страница, на которую будут переброшены не авторизованные пользователи.
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 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 |
<!DOCTYPE html> <%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8"%> <%@ taglib prefix= "c" uri= "http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix= "spring" uri= "http://www.springframework.org/tags"%> <%@ taglib prefix= "page" tagdir= "/WEB-INF/tags" %> <%@ taglib prefix= "security" uri= "http://www.springframework.org/security/tags" %> <html> <head> <meta charset= "utf-8"> <title>Login Page</title> <spring:url value= "/resources/css/bootstrap.css" var= "bootstrap" /> <spring:url value= "/resources/css/signin.css" var= "signin" /> <link href= "${bootstrap}" rel= "stylesheet" /> <link href= "${signin}" rel= "stylesheet" /> </head> <body> <form name= "form" action= "j_spring_security_check" method= "post" class= "form-signin"> <security:authorize access= "hasAnyRole('ROLE_ADMIN','ROLE_SUPER_USER', 'ROLE_USER')" var= "isUSer"/> <font size= "2" color= "red"> <c:if test= "${not isUSer}"> <c:if test= "${empty param.error}"> Вы не вошли </c:if> </c:if> </font> <font size= "2" color= "green"> <c:if test= "${isUSer}">Вы вошли как: <security:authentication property= "principal.username"/> с ролью: <b><security:authentication property= "principal.authorities"/></b> </c:if> </font> <br/> <c:if test= "${not empty param.error}"> <font size= "2" color= "red"><b>Неправильный логин или пароль</b></font> </c:if> <h2 class= "form-signin-heading">Пожалуйста войдите</h2> <label for= "inputEmail" class= "sr-only"><spring:message code= "email" text= "Email"/></label> <input id= "inputEmail" class= "form-control" name= "j_username" value= "admin@gmail.com" required autofocus/> <label for= "inputPassword" class= "sr-only"><spring:message code= "pass" text= "Password"/></label> <input type= "password" id= "inputPassword" class= "form-control" name= "j_password" value= "12345" required/> <div class= "checkbox"> <label> <input type= "checkbox" id= "rememberme" name= "_spring_security_remember_me"/>Запомнить меня </label> </div> <input type= "submit" value= "Войти" class= "btn btn-lg btn-primary btn-block" > <br/> <a href= "javascript:history.back()">Назад</a> <br /><br /> <p>Доступные роли:</p> <b>ROLE_SUPER_USER</b><br /> Login:<span style= "color: royalblue">superuser@outlook.com</span> Password: <span style= "color: royalblue">12345</span> <br /> <b>ROLE_ADMIN</b> <br /> Login:<span style= "color: royalblue">admin@gmail.com</span> Password: <span style= "color: royalblue">12345</span> <br /> <b>ROLE_USER</b> <br /> Login: <span style= "color: royalblue">roleuser@outlook.com</span> Password: <span style= "color: royalblue">12345</span> </form> </body> </html> |
Здесь стоит обратить внимание на использование значений атрибутов, которые были указаны в файле конфигурации Spring Security security-config.xml. Повторять описание не буду, а просто выделю их в отдельной записи:
1 2 3 4 5 6 7 8 9 |
<form name= "form" action= "j_spring_security_check" method= "post" class= "form-signin"> <security:authorize access= "hasAnyRole('ROLE_ADMIN','ROLE_SUPER_USER', 'ROLE_USER')" var= "isUSer"/> <input id= "inputEmail" class= "form-control" name= "j_username" value= "admin@gmail.com" required autofocus/> <input type= "password" id= "inputPassword" class= "form-control" name= "j_password" value= "12345" required/> <input type= "checkbox" id= "rememberme" name= "_spring_security_remember_me"/>Запомнить меня |
6.3. Представление security.jsp для контроллера SecurityController
Для вышеописанного контроллера используется страница security.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 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 |
<!DOCTYPE html> <%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8"%> <%@ taglib prefix= "c" uri= "http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix= "spring" uri= "http://www.springframework.org/tags"%> <%@ taglib prefix= "security" uri= "http://www.springframework.org/security/tags" %> <%@ taglib prefix= "page" tagdir= "/WEB-INF/tags" %> <page:template> <jsp:body> <c:url value= "/adminOrSuperUserCanCall" var= "adminOrSuperUserCanCall" /> <c:url value= "/userOrAdminCanCallSpEL" var= "userOrAdminCanCallSpEL" /> <c:url value= "/adminMethodSecured" var= "adminMethodSecured" /> <c:url value= "/security/admin.html" var= "admin" /> <!-- Page Content --> <div class= "container"> <!-- Page Heading/Breadcrumbs --> <div class= "row"> <div class= "col-lg-12"> <h1 class= "page-header">Security в Spring <small>защита приложения</small> </h1> <ol class= "breadcrumb"> <li><a href= "index.html">Home</a> </li> <li class= "active">Security sidebar page</li> </ol> </div> </div> <!-- /.row --> <!-- Content Row --> <div class= "row"> <!-- Sidebar Column --> <div class= "col-md-3"> <div class= "list-group"> <a href= "index.html" class= "list-group-item">Home</a> <a href= "${adminOrSuperUserCanCall}" class= "list-group-item">Админ или Супер юзер</a> <a href= "${userOrAdminCanCallSpEL}" class= "list-group-item">Админ или юзер (SpEL)</a> <a href= "${adminMethodSecured}" class= "list-group-item">Только админ</a> <a href= "${admin}" class= "list-group-item">admin.jsp</a> </div> </div> <!-- Content Column --> <div class= "col-md-9"> <p> После этого текста идет область контента только для зарегестрированных пользователей: <security:authorize access= "hasAnyRole('ROLE_ADMIN','ROLE_USER', 'ROLE_SUPER_USER')"></p> <b>Вы вошли как:</b> <security:authentication property= "principal.username"/> с ролью: <b><security:authentication property= "principal.authorities"/> </b> <br /> <span style= "color: #568C00;"><security:authentication property= "principal.username"/></span> <a style= "color: #568C00!important;" href= "<c:url value= "/j_spring_security_logout"/>">Logout</a> </security:authorize> <p>Конец области контента для зарегистрированных пользователей.</p> <br /> <h3>Spring Security examples</h3> <p><a href= "${adminOrSuperUserCanCall}">@RolesAllowed(value={"ROLE_SUPER_USER","ROLE_ADMIN"}) JSR Security</a></p> <br /> <p> <a href= "${userOrAdminCanCallSpEL}" >@PreAuthorize("hasRole('ROLE_ADMIN') || hasRole('ROLE_SUPER_USER') || hasRole('ROLE_USER')") SpEL Spring Security</a> </p><br /> <p> <a href= "${adminMethodSecured}">@Secured(value={"ROLE_ADMIN"}) JSR Security</a> </p> <br /> <p> <a href= "${admin}">Только для Admin role на страницу admin.jsp</a> <br /> </div> </div> <!-- /.row --> <hr> </div> <!-- /.container --> </jsp:body> </page:template> |
С помощью нее можно перейти по защищенным ссылкам и посмотреть на результат работы Spring Security.
- Будет вызван метод adminOrSuperUserCanCall()
- Будет вызван метод userOrAdminCanCall()
- Будет вызван метод adminMethodSecured()
- Будет совершен переход по ссылке /security/admin.html. Она удовлетворяет паттерну из файла конфигурации <intercept-url pattern = «/security/**» access= «hasRole(‘ADMIN’)» />. Таким образом, если пользователь не обладает ролью ADMIN, то его перекинет на страницу login.jsp.
6.4 error.jsp
На эту страницу будет попадать пользователь, если вызовет первые три метода из пункта выше, но при этом не будет удовлетворять условиям, которые проверяются перед вызовом метода.
error.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 40 41 42 |
<!DOCTYPE html> <%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8"%> <%@ taglib prefix= "c" uri= "http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix= "spring" uri= "http://www.springframework.org/tags"%> <%@ taglib prefix= "page" tagdir= "/WEB-INF/tags" %> <page:template> <jsp:body> <!-- Page Content --> <div class= "container"> <div class= "row"> <div class= "col-lg-12"> <h1 class= "page-header">Страница ошибки Error <small>exception handler</small> </h1> <ol class= "breadcrumb"> <li><a href= "index.html">Home</a> </li> <li class= "active">Error</li> </ol> </div> </div> <!-- /.row --> <!-- Content Row --> <div class= "row"> <div class= "col-lg-12"> <p>${exceptionMsg}</p> </div> </div> <!-- /.row --> <hr> </div> <!-- /.container --> </jsp:body> </page:template> |
А если всё хорошо, то увидим другие надписи.
profile.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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<!DOCTYPE html> <%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8"%> <%@ taglib prefix= "c" uri= "http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix= "spring" uri= "http://www.springframework.org/tags"%> <%@ taglib prefix= "security" uri= "http://www.springframework.org/security/tags" %> <%@ taglib prefix= "page" tagdir= "/WEB-INF/tags" %> <page:template> <jsp:body> <!-- Page Content --> <div class= "container"> <!-- Page Heading/Breadcrumbs --> <div class= "row"> <div class= "col-lg-12"> <h1 class= "page-header">Работа Spring Security <small>доступ только для зарегистрированных пользователей</small> </h1> <ol class= "breadcrumb"> <li><a href= "index.html">Home</a> </li> <li class= "active">Spring Security</li> </ol> </div> </div> <!-- /.row --> <c:url value= "/uploadFile" var= "fileUploadControllerURL" /> <!-- Content Row --> <div class= "row"> <div class= "col-lg-12"> <p>Данные о пользователе из Spring Security. Текст ниже доступен только ролям супер юзеру, админу, или юзеру: </p> <security:authorize access= "hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER_USER', 'ROLE_USER')"> <b>Вы вошли как:</b> <security:authentication property= "principal.username"/> с ролью: <b><security:authentication property= "principal.authorities"/> </b> <br /> <br /> <br /> <p>Ссылка logout имеет атрибут <span style= "color: #0080c0;">/j_spring_security_logout</span>, который прописан в security-config.xml</p> <span style= "color: #568C00;"><security:authentication property= "principal.username"/></span> <a style= "color: red;" href= "<c:url value= "/j_spring_security_logout"/>">Logout</a> </security:authorize> </div> </div> <!-- /.row --> <hr> </div> <!-- /.container --> </jsp:body> </page:template> |
6.5 admin.jsp
Для авторизованного пользователя с ролью ADMIN страница будет выглядеть так.
Без авторизации клиента перекинет на страницу авторизации. А в случае авторизованного пользователя, но с другой ролью (например USER) страница будет выглядеть так:
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 |
<!DOCTYPE html> <%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8"%> <%@ taglib prefix= "c" uri= "http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix= "spring" uri= "http://www.springframework.org/tags"%> <%@ taglib prefix= "security" uri= "http://www.springframework.org/security/tags" %> <%@ taglib prefix= "page" tagdir= "/WEB-INF/tags" %> <page:template> <jsp:body> <!-- Page Content --> <div class= "container"> <!-- Page Heading/Breadcrumbs --> <div class= "row"> <div class= "col-lg-12"> <h1 class= "page-header">Страница с ограниченным доступом <small>только для роли admin</small> </h1> <ol class= "breadcrumb"> <li><a href= "index.html">Home</a> </li> <li class= "active">Пример работы Spring Security hasRole('Admin')</li> </ol> </div> </div> <!-- /.row --> <c:url value= "/uploadFile" var= "fileUploadControllerURL" /> <!-- Content Row --> <div class= "row"> <div class= "col-lg-12"> <p>Данные о пользователе из Spring Security. Текст ниже доступен только ролям супер юзер и админ: </p> <security:authorize access= "hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER_USER')"> <b>Вы вошли как:</b> <security:authentication property= "principal.username"/> с ролью: <b><security:authentication property= "principal.authorities"/> </b> <br /> <br /> <br /> <p>Ссылка logout имеет атрибут /j_spring_security_logout, который прописан в security-config.xml</p> <span style= "color: #568C00;"><security:authentication property= "principal.username"/></span> <a style= "color: red;" href= "<c:url value= "/j_spring_security_logout"/>">Logout</a> </security:authorize> </div> </div> <!-- /.row --> <hr> </div> <!-- /.container --> </jsp:body> </page:template> |
Примерно так можно начинать работать с Spring Security в приложении Spring MVC.
Исходные коды
MVC_AngularJS_Html5 full project — полный проект Spring MVC + AngularJS + Bootstrap + HTML5.
15. Security — код для этой части