Тема безопасности в современных реалиях крайне важна. Защите персональных данных уделяют много средств все передовые IT компании. Это вторая часть из цикла статей посвященных изучению взаимодействия технологий Spring и Vaadin.
В первой части мы научились использовать фильтры Spring Security, добавлять пользователей и их роли, а также авторизировать их. Но поставленная задача до сих пор не выполнена:
Список пользователей будет находится в БД, соответственно авторизацию нужно сверять с базой.
Давайте приступим к реализации этой задачи.
В первую очередь определимся со структурой данных. Добавим две entity:
User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package ru.javainside.entities import javax.persistence.Column import javax.persistence.Entity import javax.persistence.FetchType import javax.persistence.Id import javax.persistence.OneToMany import javax.persistence.Table @Entity @Table(name = 'users') class User implements Serializable { public static final Long serialVersionUID = 1L @Id @Column(unique = true, nullable = false, length = 45) String username @Column(nullable = false, length = 60) String password @OneToMany(fetch = FetchType.EAGER, mappedBy = "user") Set<userrole> userRole</userrole> } |
и UserRole
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package ru.javainside.entities import javax.persistence.* @Entity @Table(name = "user_roles", uniqueConstraints = @UniqueConstraint( columnNames = [ "role", "username" ])) public class UserRole implements Serializable { public static final Long serialVersionUID = 1L @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true, nullable = false) Long userRoleId; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "username", nullable = false) User user; @Column(nullable = false, length = 45) String role; } |
Для доступа к данным воспользуемся Spring Data:
1 2 3 4 5 6 7 |
package ru.javainside.repositories import org.springframework.data.repository.CrudRepository import ru.javainside.entities.User interface UserRepository extends CrudRepository&lt;User,String&gt; { } |
1 2 3 4 5 6 7 |
package ru.javainside.repositories import org.springframework.data.repository.CrudRepository import ru.javainside.entities.UserRole interface UserRoleRepository extends CrudRepository&lt;UserRole,Long&gt; { } |
Теперь необходимо реализовать интерфейс
1 |
UserDetailsService |
который является поставщиком
1 |
UserDetails |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package ru.javainside.service 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 ru.javainside.entities.UserRole import ru.javainside.repositories.UserRepository @Service('userDetailsService') class UserDetailsServiceImpl implements UserDetailsService { @Autowired UserRepository userRepository @Override UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { def user = userRepository.findOne(username) if (user == null) throw new UsernameNotFoundException('User with name ' + username + ' not found') return new org.springframework.security.core.userdetails.User( user.username, user.password, buildUserAuthority(user.userRole) ) } Collection<!--? extends GrantedAuthority--> buildUserAuthority(Set<userrole> userRoles) { Set<grantedauthority> setAuths = new HashSet<grantedauthority>();</grantedauthority></grantedauthority></userrole> for (UserRole userRole : userRoles) { setAuths.add(new SimpleGrantedAuthority(userRole.getRole())); } return new ArrayList<grantedauthority>(setAuths); } } |
Из реализации видно, что при попытке авторизации в базе по ключу username будет искаться user, если его нет, то будет вызвано
1 |
UsernameNotFoundException |
А дальше все тривиально, создается экземпляр UserDetails по login, passowrd и ролям user.
Теперь заменяем конфигурацию списка пользователей в Spring Security на:
1 2 3 4 5 6 7 |
@Autowired UserDetailsService service @Autowired void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(service) } |
Заполняем базу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
@Autowired UserRepository userRepository @Autowired UserRoleRepository userRoleRepository @Override public void run(String... args) throws Exception { def user = new User( username: 'user', password: 'user' ) def admin = new User( username: 'admin', password: 'admin' ) userRoleRepository.deleteAll() userRepository.deleteAll() userRepository.save(user) userRepository.save(admin) userRoleRepository.save( new UserRole( user: user, role: 'ROLE_USER' ) ) userRoleRepository.save( new UserRole( user: admin, role: 'ROLE_USER' ) ) userRoleRepository.save( new UserRole( user: admin, role: 'ROLE_ADMIN' ) ) } |
Оставляем старое подключение
1 2 |
def authentication = new UsernamePasswordAuthenticationToken('user', 'password') SecurityContextHolder.context.authentication = manager.authenticate(authentication) |
Проверим как оно работает:
1 |
org.springframework.security.authentication.BadCredentialsException: Bad credentials |
Поймали exception, как и ожидалось меняем password на ‘user’ и …
Вы прекрасны.