Зміст
У цій статті буде розказано як привести до довільного виду сторінку логіна в 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 р. 05:36
|
web.xml от своего предшественника отличается тем, что убраны комментарии.
Не очень функциональное изменение. Я думаю не стоило бы даже писать, что он изменился, или написали бы, что вот он конкретно(дальше не дочитал еще) - не изменился. |
20 жовтня 2013 р. 05:41
|
а в идеале - чтобы вы подсвечивали изменившиеся строки
чтоб я после предыдущей статьи сюда пришел и не копировал бессмысленно файлы/сравнивал с предыдущими а просто увидел разницу |