Выбрать главу

package tacos.security;

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;

import tacos.User;

import tacos.data.UserRepository;

@Service

public class UserRepositoryUserDetailsService implements UserDetailsService {

 private UserRepository userRepo;

 @Autowired

 public UserRepositoryUserDetailsService(UserRepository userRepo) {

   this.userRepo = userRepo;

 }

 @Override

 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

   User user = userRepo.findByUsername(username);

   if (user != null) {

     return user;

   }

   throw new UsernameNotFoundException("User '" + username + "' not found");

 }

}

UserRepositoryUserDetailsService внедряется с экземпляром UserRepository через его конструктор. Затем в методе loadByUsername(), вызывается findByUsername () объекта UserRepository для поиска User.

Метод loadByUsername () имеет одно простое правило: он никогда не должен возвращать null. Поэтому, если вызов findByUsername() возвращает null, loadByUsername() будет бросать UsernameNotFoundException. В противном случае будет возвращен найденный пользователь.

Вы видите, что UserRepositoryUserDetailsService аннотируется @Service. Это еще одна из аннотаций стереотипа Spring, которые помечают его для включения в сканирование компонентов Spring, поэтому нет необходимости явно объявлять этот класс как bean. Spring автоматически обнаружит его и создаст его как bean.

Однако по-прежнему необходимо настроить сервис сведений о пользователе с Spring Security. Таким образом, вы вернетесь к методу configure() еще раз:

@Autowired

private UserDetailsService userDetailsService;

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

 auth.userDetailsService(userDetailsService);

}

На этот раз просто вызовите метод userDetailsService (), передав экземпляр UserDetailsService, который был autowired к SecurityConfig.

Как и при аутентификации на основе JDBC, вы можете (и должны) также настроить кодировщик паролей, чтобы пароль мог быть закодирован в базе данных. Это можно сделать, сначала объявив bean типа PasswordEncoder, а затем внедрив (injecting) его в конфигурацию сервиса сведений о пользователе, вызвав passwordEncoder():

@Bean

public PasswordEncoder encoder() {

 return new StandardPasswordEncoder("53cr3t");

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

 auth.userDetailsService(userDetailsService)

   .passwordEncoder(encoder());

}

Важно обсудить последнюю строку в методе configure(). Казалось бы, вы вызываете метод encoder() и передаете его возвращаемое значение в passwordEncoder(). В действительности, однако, поскольку метод encoder() аннотируется @Bean, он будет использоваться для объявления компонента PasswordEncoder в контексте приложения Spring. Любые вызовы encoder() будут перехвачены, чтобы возвратить экземпляр bean из контекста приложения.

РЕГИСТРАЦИЯ ПОЛЬЗОВАТЕЛЕЙ

Несмотря на то, что Spring Security обрабатывает многие аспекты безопасности, она не принимает непосредственного участия в процессе регистрации пользователей, поэтому вы будете полагаться на Spring MVC для обработки этой задачи. Класс RegistrationController в следующем листинге представляет и обрабатывает регистрационные формы.

Листинг 4.7 Контроллер регистрации пользователей

package tacos.security;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import tacos.data.UserRepository;

@Controller

@RequestMapping("/register")

public class RegistrationController {

 private UserRepository userRepo;

 private PasswordEncoder passwordEncoder;

 public RegistrationController(UserRepository userRepo, PasswordEncoder passwordEncoder) {

     this.userRepo = userRepo;

     this.passwordEncoder = passwordEncoder;

 }

 @GetMapping

 public String registerForm() {

   return "registration";

 }

 @PostMapping

 public String processRegistration(RegistrationForm form) {

   userRepo.save(form.toUser(passwordEncoder));

   return "redirect:/login";

 }

}

Как любой типичный Spring  MVC controller, RegistrationController аннотирован @Controller для того чтобы обозначить его как controller и отметить его для сканирования компонентов (component  scanning). Он также аннотируется @RequestMapping, так что он будет обрабатывать запросы, путь которых /register.

В частности, запрос GET для /register будет обрабатываться методом registerForm (), который просто возвращает логическое имя страницы регистрации. В следующем списке показан шаблон Thymeleaf, определяющий страницу регистрации.