Вернуться в список

Spring Security. Страница Login

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

В этой статье как основа используется пример из предыдущей статьи "Введение в Spring Security. Hello World!". Создайте проект, следуя инструкциям из статьи, или скачайте файл проекта для его использования в этой статье.

В предыдущей статье упоминалось, что форма логина генерируется автоматически фреймворком Spring Security. Поэтому в проекте отсутствовали jsp-файлы, отвечающие за страницу логина. Тем не менее, у Вас есть возможность привести эту страницу к любому виду, изменив конфигурацию http в файле application-security.xml следующим образом:

<http auto-config ="true">
    <intercept-url pattern ="/secure/**" access ="ROLE_USER" />
    <intercept-url pattern ="/admin/**" access ="ROLE_ADMIN" />
    <intercept-url pattern ="/login.jsp*" access ="IS_AUTHENTICATED_ANONYMOUSLY"/>
    <form-login login-page ="/signin"
      authentication-failure-url ="/signin-failure" default-target-url ="/" />
</http>

В случае неверного логина/пароля пользователь будет автоматически перенаправлен на страницу /signin-failure, что описывается атрибутом authentication-failure-url тега form-login.

Далее нам также понадобится изменить блок authentication-manager. Вместо:

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name ="admin" password ="adminpassword" authorities ="ROLE_USER, ROLE_ADMIN" />
            <user name ="user" password ="userpassword" authorities ="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

добавьте следующий код:

<authentication-manager>
    <authentication-provider user-service-ref ="customUserDetailsService">
    </authentication-provider>
</authentication-manager>

Упомянутый сервис customUserDetailsService нужен для обработки данных (логина/пароля), полученных от пользователя. Исходный код класса CustomUserDetailsService будет представлен позже, а пока приведем измененные файлы конфигурации.

web.xml

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

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/root-context.xml
            /WEB-INF/spring/application-security.xml
        </param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <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/appServlet/servlet-context.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>

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

root-context.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/mvc"
 xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans ="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-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
 <annotation-driven />

 <resources mapping ="/resources/**" location ="/resources/" />

 <beans:bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <beans:property name ="prefix" value ="/WEB-INF/views/" />
     <beans:property name ="suffix" value =".jsp" />
 </beans:bean>
 
 <context:component-scan base-package ="com.seostella.springsecuritylogin" />
   
</beans:beans>

application-security.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/security"
 xmlns:beans ="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-3.0.xsd
http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <http pattern ="/css/**" security ="none" />

 <http auto-config ="true">
     <intercept-url pattern ="/secure/**" access ="ROLE_USER" />
     <intercept-url pattern ="/admin/**" access ="ROLE_ADMIN" />
     <intercept-url pattern ="/login" access ="IS_AUTHENTICATED_ANONYMOUSLY" />
     <form-login login-page ="/signin"
      authentication-failure-url ="/signin-failure" default-target-url ="/" />
 </http>

 <authentication-manager>
     <authentication-provider user-service-ref ="customUserDetailsService">
     </authentication-provider>
 </authentication-manager>
 
</beans:beans>

servlet-context.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/mvc"
 xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans ="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-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans:beans>

Итак, возвращаемся к CustomUserDetailsService, его код выглядит следующим образом:

package com.seostella.springsecuritylogin.service;

import org.springframework.beans.factory.annotation.Autowired;
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;

@Service
public class CustomUserDetailsService implements UserDetailsService {
    
    @Autowired
     private UserManager userManager;
    
     public UserDetails loadUserByUsername(String username)
       throws UsernameNotFoundException {
         return userManager.getUser(username);
       }

}

Пользовательский сервис должен быть унаследован от интерфейса UserDetailsService. Единственный метод этого интерфейса, который нужно реализовать, называется loadUserByUsername. Этот метод должен возвращать объект, который реализует интерфейс UserDetails. Такой класс у нас будет называться User.

package com.seostella.springsecuritylogin.domain;

import java.util.Collection;
import java.util.HashSet;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class User implements UserDetails {
     private static final long serialVersionUID = 8266525488057072269L;
     private String username;
     private String password;
     private Collection<GrantedAuthority> authorities;

     public User(String username, String password, String roles) {
         super ();
         this.username = username;
         this.password = password;
         this.setRoles(roles);
       }

     public void setRoles(String roles) {
         this.authorities = new HashSet<GrantedAuthority>();
         for (final String role : roles.split(",")) {
             if (role != null && !"".equals(role.trim())) {
                GrantedAuthority grandAuthority = new GrantedAuthority() {
                     private static final long serialVersionUID = 3958183417696804555L;

                     public String getAuthority() {
                         return role.trim();
                       }
                   };
                 this.authorities.add(grandAuthority);
               }
           }
       }

     public void setUsername(String username) {
         this.username = username;
       }

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

     public Collection< ? extends GrantedAuthority> getAuthorities() {
         return this.authorities;
       }

     public String getPassword() {
         return password;
       }

     public String getUsername() {
         return username;
       }

     public boolean isAccountNonExpired() {
         return true;
       }

     public boolean isAccountNonLocked() {
         return true;
       }

     public boolean isCredentialsNonExpired() {
         return true;
       }

     public boolean isEnabled() {
         return true;
       }
}

Код класса UserManager:

package com.seostella.springsecuritylogin.service;

import java.util.HashMap;

import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.seostella.springsecuritylogin.domain.User;

@Service
public class UserManager {
     private HashMap<String, User> users;

     public UserManager() {
        users = new HashMap<String, User>();
        users.put("john"new User("john""1""ROLE_USER"));
        users.put("bob"new User("bob""2""ROLE_USER, ROLE_ADMIN"));
       }
    
     public User getUser(String username) throws UsernameNotFoundException {
         if ( !users.containsKey( username ) ) {
             throw new UsernameNotFoundException( username + " not found" );
           }
        
         return users.get( username );  
       }
}

В конструкторе представленного сервиса создается два объекта User с именами john и bob. Конечно, на месте статического создания объектов можно было бы сделать метод для извлечения пользователей из базы данных. Но это привело бы к усложнению примера, чего мы пытаемся избежать.

Контроллер для обработки действий /signin и /signin-failure представлен ниже:

package com.seostella.springsecuritylogin.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SigninController {
    @RequestMapping(value = "/signin", method = RequestMethod.GET)
     public String signin() {
         return "user/signin";
       }
    
    @RequestMapping(value = "/signin-failure", method = RequestMethod.GET)
     public String signinFailure() {
         return "user/signin_failure";
       }
    
}

И, наконец, jsp-файл signin.jsp, содержащий форму логина:

<%@ taglib uri ="http://java.sun.com/jsp/jstl/core" prefix ="c"%>
   <%@ taglib uri ="http://www.springframework.org/tags/form" prefix ="form"%>
      <%@ taglib uri ="http://www.springframework.org/tags" prefix ="spring"%>
         <%@ page session ="true"%>
            <html>
               <head>
                  <title>Sign In</title>
               </head>
               <body>
                   <h1>Spring Security - Sign In</h1>

                   <div style ="color: red">${message}</div>
                   
                   <form class ="login-form" action ="j_spring_security_check" method ="post">
                        <label for ="j_username">Username: </label>
                         <input id ="j_username" name ="j_username" size ="20" maxlength ="50" type ="text" />

                        <label for ="j_password">Password: </label>
                        <input id ="j_password" name ="j_password" size ="20" maxlength ="50" type ="password" />
                        
                        <input type ="submit" value ="Login" />
                   </form>
               </body>
            </html>

Несколько пояснений по поводу формы. Первое, действие формы (атрибут action) должно быть j_spring_security_check. Второе, название полей для логина и пароля должны быть j_username и j_password соответственно.

Файл signin_failure.jsp:

<%@ taglib uri ="http://java.sun.com/jsp/jstl/core" prefix ="c"%>
   <%@ taglib uri ="http://www.springframework.org/tags/form" prefix ="form"%>
      <%@ taglib uri ="http://www.springframework.org/tags" prefix ="spring"%>
         <%@ page session ="true"%>
            <html>
               <head>
                  <title>Sign In</title>
               </head>
               <body>
                   <h1>Spring Security - Sign In Failure</h1>
               </body>
            </html>

Если Вы хотите использовать стандартный HTTP-диалог для авторизации и отказаться от формы логина, измените http-блок конфигурации безопасности следующим образом:

<http auto-config ="true">
    <intercept-url pattern ="/secure/**" access ="ROLE_USER" />
    <intercept-url pattern ="/admin/**" access ="ROLE_ADMIN" />
   <!--  <intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" /> -->
   <!--  <form-login login-page="/signin" -->
   <!--  authentication-failure-url="/signin-failure" default-target-url="/" /> -->
    <http-basic />
</http>

Если Вы хотите чтобы пользователь всегда перенаправлялся на страницу, указанную в атрибуте default-target-url - измените http-блок следующим образом:

<http auto-config ="true">
    <intercept-url pattern ="/secure/**" access ="ROLE_USER" />
    <intercept-url pattern ="/admin/**" access ="ROLE_ADMIN" />
    <intercept-url pattern ="/login.jsp*" access ="IS_AUTHENTICATED_ANONYMOUSLY"/>
    <form-login login-page ="/signin"
      authentication-failure-url ="/signin-failure" default-target-url ="/" always-use-default-target ="true" />
</http>

Скачать проект, демонстрирующий вышеприведенный пример, Вы можете используя следующую ссылку Скачать spring-security-login.zip

23.08.2015

    Только зарегистрированные пользователи могут оставить комментарий.
    Вернуться в список
    2017 «Инфокристалл» Сборка от 18.04.2017 03:58
    Контакты | Задать вопрос

    Карта сайта