Быстрый старт в Spring Security – Devcolibri – Android для начинающих

Быстрый старт в Spring Security

В этом уроке будет рассмотрен пример реализации аутентификации пользователя используя Spring Security.

Шаг 1. Создание проекта и подключение зависимостей

Начнем с традиционного создания проекта:

Создали Maven проект, теперь добавим зависимости в pom.xml их будет довольно таки много, поэтому прикладываю полный pom файл:

<?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>spring-security-exam</groupId>
    <artifactId>spring-security-exam</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <spring.mvc>4.0.0.RELEASE</spring.mvc>
        <javax.servlet>3.0.1</javax.servlet>
        <jstl.version>1.2</jstl.version>
        <spring.securiry>3.2.0.RELEASE</spring.securiry>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.mvc}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.mvc}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${javax.servlet}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.securiry}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.securiry}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.securiry}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.securiry}</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>secure-exam</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

По каждому из зависимостей не буду останавливаться, но включил самые обязательные зависимости для реализации Spring Security.

Сразу же покажу вам какая должна быть структура проекта, чтобы вы не терялись в создании классов и файлов.

Шаг 2. Начинаем с конца, делаем внешний вид

Для большей привлекательности как к внешнему виду, так и мотивации изучить этот урок, я решил использовать Bootstrap 3.

Скачиваем bootstrap и в проекте создаем папку webapp/pages/ и копируем туда файлы bootstrap-а. (смотрите на структуре проекта)

Теперь создаем webapp/pages/index.jsp:

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Spring Security</title>

    <!-- Bootstrap core CSS -->
    <link href="<c:url value="/pages/css/bootstrap.css" />" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="<c:url value="/pages/css/jumbotron-narrow.css" />" rel="stylesheet">

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
    <![endif]-->
</head>

<body>

<div class="container">

    <div class="jumbotron" style="margin-top: 20px;">
        <h1>Devcolibri.com</h1>
        <p class="lead">
            Devcolibri - это сервис предоставляющий всем желающим возможность обучаться программированию.
        </p>
        <sec:authorize access="!isAuthenticated()">
            <p><a class="btn btn-lg btn-success" href="<c:url value="/login" />" role="button">Войти</a></p>
        </sec:authorize>
        <sec:authorize access="isAuthenticated()">
            <p>Ваш логин: <sec:authentication property="principal.username" /></p>
            <p><a class="btn btn-lg btn-danger" href="<c:url value="/logout" />" role="button">Выйти</a></p>

        </sec:authorize>
    </div>

    <div class="footer">
        <p>© Devcolibri 2014</p>
    </div>

</div>
</body>
</html>

Выглядеть эта страница будет так:

Создаем следующую страницу на которой собственно и будет происходить вход на сайт webapp/pages/login.jsp:

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Spring Security</title>

    <!-- Bootstrap core CSS -->
    <link href="<c:url value="/pages/css/bootstrap.css" />" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="<c:url value="/pages/css/signin.css" />" rel="stylesheet">

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
    <![endif]-->
</head>

<body>

<div class="container" style="width: 300px;">
    <c:url value="/j_spring_security_check" var="loginUrl" />
    <form action="${loginUrl}" method="post">
        <h2 class="form-signin-heading">Please sign in</h2>
        <input type="text" class="form-control" name="j_username" placeholder="Email address" required autofocus value="colibri">
        <input type="password" class="form-control" name="j_password" placeholder="Password" required value="1234">
        <button class="btn btn-lg btn-primary btn-block" type="submit">Войти</button>
    </form>
</div>

</body>
</html>

Тут обратите особое внимание на обя тега input, а именно на их name:

name=”j_username

name=”j_password

В данном случае эти поля должны быть именно с такими значениями.

А также вы возможно уже увидели эту строку:

<c:url value="/j_spring_security_check" var="loginUrl" />

так мы создали переменную, которая будет хранить ссылку на security check, он выполняет аутентификация, где значение value обязательно должно быть таким.

Выглядеть она будет так:

3810_4

Думаю получилось не плохо :)

Шаг 3. Создаем контроллеры

О создании MVC проекта на Spring можно почитать урок Spring MVC Hello World

Создаем пакет controller и класс MainController:

package com.devcolibri.secure.controller;

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
@RequestMapping("/")
public class MainController {

    @RequestMapping(method = RequestMethod.GET)
    public String start(Model model){
        return "index";
    }

}

Этот контроллер будет просто направлять пользователя на страницу index.jsp.

А теперь создадим второй аналогичный контроллер, который будет показывать пользователю страничку login.jsp.

Создаем класс и называем его LoginController:

package com.devcolibri.secure.controller;

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
@RequestMapping("/login")
public class LoginController {

    @RequestMapping(method = RequestMethod.GET)
    public String loginPage(Model model){
        return "login";
    }

}

Теперь мы сможем ходить по страничкам.

Шаг 4. Конфигурирование Spring MVC

Теперь нам нужно сконфигурировать Spring MVC чтобы он имел возможность разворачивать свой контекс и мы могли получать доступ к нашим бинам.

Создаем в пакете config класс WebAppConfig и наследуем его от WebMvcConfigurerAdapter:

package com.devcolibri.secure.config;

import com.devcolibri.secure.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.devcolibri")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
        UrlBasedViewResolver resolver = new UrlBasedViewResolver();
        resolver.setPrefix("/pages/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);

        return resolver;
    }

}

Подробную информацию о Java Config для Spring вы можете посмотреть в уроке Spring 3. JavaConfig на примере Spring MVC.

Шаг 5. Создание Entity

Так как цель урока показать пример использования Spring Security, то я не стал реализовывать работу с БД, а всего лишь создал все необходимое для этого, чтобы в будущем реализовать Spring DATA.

Создаем entity по сути это простой класс, так как мы не используем не ORM фреймворков, назовем его User:

package com.devcolibri.secure.entity;

public class User {

    private String login;
    private String password;

    public User(String login, String password) {
        this.login = login;
        this.password = password;
    }

    public User() {

    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

В будущем вы можете этот объект пополнять своими свойствами, но для примера нам достаточно знать Login (логин) и Password (пароль).

И еще в этом же пакете создадим пакет enums и в нем создаем enum UserRoleEnum, который будет определять роли пользователя:

package com.devcolibri.secure.entity.enums;

public enum UserRoleEnum {

    ADMIN,
    USER,
    ANONYMOUS;

    UserRoleEnum() {
    }

}

Теперь у нас есть 3 роли, которые мы немного позже будем использовать.

Шаг 6. Создаем слой Services

Обычно проекты имеют несколько уровней слоёв, о которых я еще напишу статью, но один из этих слоёв мы реализуем прям сейчас.

Нам нужно реализовать Service tier (сервис слой либо уровень обслуживания). Создаем пакет service и в нем создаем интерфейс UserService:

package com.devcolibri.secure.service;

import com.devcolibri.secure.entity.User;

public interface UserService {

    User getUser(String login);

}

Этот сервисный интерфейс говорит о том, что у нас будет сервис который позволит получать User не важно как и откуда, но он позволит нам это.

А теперь давайте напишем первую реализацию данного интерфейса, для этого создаем на том же уровне класс UserServiceImpl:

package com.devcolibri.secure.service;

import com.devcolibri.secure.entity.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getUser(String login) {
        User user = new User();
        user.setLogin(login);
        user.setPassword("7110eda4d09e062aa5e4a390b0a572ac0d2c0220");

        return user;
    }

}

Как видите реализация довольно простая, тут мы просто заполняем объект User используя setters, но мы также можем в этом сервис методе вызвать метод из DAO, который бы достал нам этого юзера с БД например либо получил бы его с Web Service.

Обратите внимание, что я установил в поле Password специфичный пароль, а именно зашифрованный в SHA1 формате.

То есть я взял пароль “1234” и зашифровал его в SHA1 формат с помощью online сервиса SHA1 online generator и получил уже зашифрованный пароль “7110eda4d09e062aa5e4a390b0a572ac0d2c0220“.

Шаг 7. Создаем реализацию UserDetailsService

Для того, чтобы связать наш сервис UserService со Spring Security, нам нужно реализовать специальный интерфейс фреймворка Spring Security который позволит выполнять аутентификацию пользователя на основании данных полученных с UserService написанного выше.

Создаем класс UserDetailsServiceImpl и реализуем его от UserDetailsService:

package com.devcolibri.secure.service;

import com.devcolibri.secure.entity.User;
import com.devcolibri.secure.entity.enums.UserRoleEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        // с помощью нашего сервиса UserService получаем User
        User user = userService.getUser("colibri");
        // указываем роли для этого пользователя
        Set<GrantedAuthority> roles = new HashSet();
        roles.add(new SimpleGrantedAuthority(UserRoleEnum.USER.name()));

        // на основании полученных данных формируем объект UserDetails
        // который позволит проверить введенный пользователем логин и пароль
        // и уже потом аутентифицировать пользователя
        UserDetails userDetails =
                new org.springframework.security.core.userdetails.User(user.getLogin(), 
                                                                       user.getPassword(), 
                                                                       roles);

        return userDetails;
    }

}

Этот сервис и является основной логикой аутентификаци c использованием Spring Security.

Шаг 8. Добавляем инициализацию бина UserDetailsServiceImpl в конфигурацию WebAppConfig

Для того чтобы наша реализация интерфейса UserDetailsService смогла инициализыроваться контейнером Spring нам нужно добавить его в WebAppConfig:

@Bean
public UserDetailsService getUserDetailsService(){
    return new UserDetailsServiceImpl();
}

После этого Spring контейнер будет знать какую реализацию интерфейса UserDetailsService нужно брать.

Шаг 9. Конфигурирование Spring Security

Теперь самое главное, заставить все это заработать :)

Создаем в пакете config класс SecurityConfig и наследуем его от WebSecurityConfigurerAdapter:

package com.devcolibri.secure.config;

import com.devcolibri.secure.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    // регистрируем нашу реализацию UserDetailsService 
    // а также PasswordEncoder для приведения пароля в формат SHA1
    @Autowired
    public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(getShaPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // включаем защиту от CSRF атак
        http.csrf()
                .disable()
                // указываем правила запросов
                // по которым будет определятся доступ к ресурсам и остальным данным
                .authorizeRequests()
                .antMatchers("/resources/**", "/**").permitAll()
                .anyRequest().permitAll()
                .and();

        http.formLogin()
                // указываем страницу с формой логина
                .loginPage("/login")
                // указываем action с формы логина
                .loginProcessingUrl("/j_spring_security_check")
                // указываем URL при неудачном логине
                .failureUrl("/login?error")
                // Указываем параметры логина и пароля с формы логина
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                // даем доступ к форме логина всем
                .permitAll();

        http.logout()
                // разрешаем делать логаут всем
                .permitAll()
                // указываем URL логаута
                .logoutUrl("/logout")
                // указываем URL при удачном логауте
                .logoutSuccessUrl("/login?logout")
                // делаем не валидной текущую сессию
                .invalidateHttpSession(true);

    }

    // Указываем Spring контейнеру, что надо инициализировать ShaPasswordEncoder
    // Это можно вынести в WebAppConfig, но для понимаемости оставил тут
    @Bean
    public ShaPasswordEncoder getShaPasswordEncoder(){
        return new ShaPasswordEncoder();
    }

}

Это базовая конфигурация, которая нужна для наших нужд, она может расширятся.

Шаг 10. Регистрация конфигураций в контексте Spring

Чтобы Spring видел наши конфигурации, а именно WebAppConfig и SecurityConfig их нужно зарегистрировать в контексте Spring.

Создаем в пакете config класс Initializer и реализуем WebApplicationInitializer:

package com.devcolibri.secure.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;

public class Initializer implements WebApplicationInitializer {

    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        // регистрация конфигураций в Spring контексте
        ctx.register(WebAppConfig.class);
        ctx.register(SecurityConfig.class);
        servletContext.addListener(new ContextLoaderListener(ctx));

        ctx.setServletContext(servletContext);

        Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}

На этом этап конфигурации проекта можно считать законченным.

B web.xml нужно добавить фильтр, который будет определять наши реквесты и проверять валидность сессии:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <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>

</web-app>

Шаг 11. Развертывание и тестирование

Пора все собрать и развернуть на сервере приложений.

Собираем проект с помощью Maven:

Теперь развертываем собранный собранный war на сервере приложений, я выбрал Tomcat 8.

Конфигурируем Intellij IDEA под Tomcat. Заходим в Edit Configuration…

Нажимаем по плюсику (Add New Configuration…) ищем там Tomcat Server -> Local:

Конфигурируем также как показано на скриншоте, потом переходим в раздел Deployment:

Жмем Add -> External Source… в корне вашего проекта будет папка target в ней будет secure-exam.war выбираем его и в поле Application context пишем /secure.

После этого запускаем. Попадем на главное окно жмем Войти переходим на форму с логином и нажимаем Войти, после этого вас перенаправит на главную страницу где вы увидите свой логин.

На этом все. Надеюсь данный урок вам поможет.

Также читайте серию статей «Spring Data JPA. Работа с БД»: часть 1, часть 2 и часть 3

ПОХОЖИЕ ПУБЛИКАЦИИ

    None Found

209290
28/01/2014

65 комментариев к статье "Быстрый старт в Spring Security"

Добавить комментарий

Сайт использует cookie-файлы для того, чтобы вам было удобнее им пользоваться. Для продолжения работы с сайтом, вам необходимо принять использование cookie-файлов.

Я ознакомлен(а)