Содержание
В этой статье будет рассказано как привести к произвольному виду страницу логина в 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 будет представлен позже, а пока приведем измененные файлы конфигурации.
<?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>
<?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>
<?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>
<?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
< | Введение в Spring Security. Hello World! | Как получить пользователя в Spring Security | > |
10 августа 2012 г. 11:40
|
Большое спасибо! Здорово помогло)
|
20 октября 2013 г. 5:36
|
web.xml от своего предшественника отличается тем, что убраны комментарии.
Не очень функциональное изменение. Я думаю не стоило бы даже писать, что он изменился, или написали бы, что вот он конкретно(дальше не дочитал еще) - не изменился. |
20 октября 2013 г. 5:41
|
а в идеале - чтобы вы подсвечивали изменившиеся строки
чтоб я после предыдущей статьи сюда пришел и не копировал бессмысленно файлы/сравнивал с предыдущими а просто увидел разницу |