Spring Security и Mysql - практический пример.
UPDATE: Возможно вам будет интересен пример использования Spring Security 4 и AngularJS.
Я уже упоминал про JavaEE фреймворк Spring Security и приводил простой пример его использования. Этот пример включает в себя создание веб приложения с двумя страницами - страница логина и закрытая страница. Для авторизации, как ясно, там используется Spring Security. Однако имя пользователя и пароль (точнее SHA-1 хэш) в этом примере хранятся в XML файле настройки контекста Spring Security. Понятно, что в реальных задачах данные пользователя, как и его роли, хранятся в базе данных.
В этом посте я хочу привести усовершенствованный пример, в котором имя пользователя, хэш пароля и роль пользователя в системе будут храниться в БД Mysql. Для основы возьму код предыдущего примера, а полный код для этого поста можно найти ниже.
Итак, как можно связать авторизацию с помощью Spring и Mysql. Сделать это можно немного по разному, посмотрим на один из способов. Первым делом установим Mysql и создадим тестовую БД, в которой будет одна таблица. В таблице содержится имя пользователя, название его роли в системе, и хэш пароля. Обычно в БД хранят и другую информацию - например дату, до которой аккаунт активен и пр. Но в этом примере этого не будет.
После создания БД создадим пользователя, имя и пароль которого будут прописаны в настройках приложения. Даем ему права и устанавливаем кодировку UTF-8 для базы. Теперь создаем таблицу и добавляем в него пользователя:
Готово, теперь в базе есть 1 пользователь с ролью ROLE_ADMIN. Настройка базы завершена, переходим к приложению. Первым делом нужно добавить в pom.xml нужные зависимости:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency>
В файл root-context.xml добавим настройки для подключения к Mysql:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- MySQL config --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test_spring?useUnicode=true&characterEncoding=utf8" /> <property name="username" value="spring_app" /> <property name="password" value="123456" /> </bean> <!-- В этом контектсе загружается все, кроме контроллеров Spring MVC--> <context:component-scan base-package="org.develnotes.examples.service"> <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" /> </context:component-scan> </beans>
Теперь добавим два класса - MysqlContentDAO и MysqlUserService.
MysqlContentDAO.java:
package org.develnotes.examples.service; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * Используется для получения данных из БД * Пример для develnotes.org * @author Alex Dl. * */ @Service public class MysqlContentDAO { private final Logger log = LoggerFactory.getLogger(MysqlContentDAO.class); @Autowired private DataSource dataSource; /** * Получить хэш SHA-1 пароля пользователя * @param username имя пользователя * @return */ public String getUserPasswordHash(String username) { String sql = "select pwdHash from users where userName=?;"; Connection conn = null; try { conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); if (rs.next()) { return rs.getString("pwdHash"); } rs.close(); ps.close(); } catch (Exception e){ log.error("Error : " + e.getMessage()); } finally { if (conn != null) { try { conn.close(); } catch (Exception e) {} } } return null; } /** * Получить роль пользователя по имени пользователя * @param username имя пользователя * @return роль пользователя */ public String getUserRole(String username) { String roleName = ""; String sql = "select userRole from users where userName=?;"; Connection conn = null; try { conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); while (rs.next()) { roleName = rs.getString("userRole"); } rs.close(); ps.close(); } catch (Exception e) { log.error("Error : " + e.getMessage()); } finally { if (conn != null) { try { conn.close(); } catch (Exception e) { } } } return roleName; } }
Этот класс используется для получения роли пользователя по имени и для получения хэша пароля. Класс MysqlUserService используется для предоставления данных Spring Security и реализует интерфейс org.springframework.security.core.userdetails.UserDetailsService:
package org.develnotes.examples.service; import java.io.Serializable; import java.util.Collection; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.stereotype.Service; /** * Сервис для получения роли пользователя/проверки пароля при входе в систему. * Пример для develnotes.org * @author Alex Dl. */ @Service public class MysqlUserService implements UserDetailsService, Serializable { private static final long serialVersionUID = -7858305254948955717L; @Autowired private MysqlContentDAO contentService; private static final Logger logger = LoggerFactory.getLogger(MysqlUserService.class); @Override public UserDetails loadUserByUsername(final String username) { //создаем простой анонимный класс, реализующий интерфейс UserDetails return new UserDetails() { private static final long serialVersionUID = 2059202961588104658L; @Override public boolean isEnabled() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isAccountNonExpired() { return true; } @Override public String getUsername() { return username; } @Override public String getPassword() { try { //получаем хэш пароля по имени пользователя return contentService.getUserPasswordHash(username); } catch (Exception e) { logger.error("Error while access MYSQL database to get pwdHash for user = " + username); } return null; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> auths = new java.util.ArrayList<SimpleGrantedAuthority>(); try { //получаем роль пользователя по имени пользователя auths.add(new SimpleGrantedAuthority(contentService.getUserRole(username))); } catch (Exception e) { logger.error("Error while access MYSQL database " + "to get details for user = " + username); } return auths; } }; } public MysqlContentDAO getContentService() { return contentService; } public void setContentService(MysqlContentDAO contentService) { this.contentService = contentService; } }
В классе выше необходимо определить один метод - loadUserByUsername (final String username), который возвращает данные учетной записи для Spring. Для упрощения примера - создаем анонимный класс, в котором некоторые поля не используются, устанавливаем туда значения по умолчанию. Теперь необходимо указать этот класс в контексте Spring Security:
<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 auto-config="true"> <intercept-url pattern="/admin" access="ROLE_ADMIN" /> <intercept-url pattern="/admin/*" access="ROLE_ADMIN" /> <intercept-url pattern="/admin*" access="ROLE_ADMIN" /> <form-login login-page="/login" default-target-url="/admin" authentication-failure-url="/loginfailed" /> <logout logout-success-url="/logout" /> </http> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="mysqlUserService"> <password-encoder hash="sha"/> </authentication-provider> </authentication-manager> </beans:beans>
Все, наше приложение готово! Запустив его на сервере приложений/контейнере сервлетов, можно проверить работоспособность.
Для пользователя admin правильным паролем является самый секьюрный - "qwerty" =):
Теги: javaEE programming Spring
comments powered by Disqus