1. Обзор
В этом уроке – мы заменим процесс аутентификации OAuth2, поддерживаемый Reddit, более простым логином на основе формы .
Мы все равно сможем подключить Reddit к приложению после входа в систему, мы просто не будем использовать Reddit для управления нашим основным потоком входа.
2. Базовая Регистрация Пользователя
Во-первых, давайте заменим старый поток аутентификации.
2.1. Сущность Пользователя
Мы внесем несколько изменений в сущность пользователя: сделаем имя пользователя уникальным, добавим пароль поле (временное):
@Entity
public class User {
...
@Column(nullable = false, unique = true)
private String username;
private String password;
...
}2.2. Зарегистрировать Нового Пользователя
Далее – давайте посмотрим, как зарегистрировать нового пользователя в бэкэнде:
@Controller
@RequestMapping(value = "/user")
public class UserController {
@Autowired
private UserService service;
@RequestMapping(value = "/register", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void register(
@RequestParam("username") String username,
@RequestParam("email") String email,
@RequestParam("password") String password)
{
service.registerNewUser(username, email, password);
}
}Очевидно, что это базовая операция создания для пользователя – никаких наворотов.
Вот фактическая реализация на уровне сервиса :
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PreferenceRepository preferenceReopsitory;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void registerNewUser(String username, String email, String password) {
User existingUser = userRepository.findByUsername(username);
if (existingUser != null) {
throw new UsernameAlreadyExistsException("Username already exists");
}
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(password));
Preference pref = new Preference();
pref.setTimezone(TimeZone.getDefault().getID());
pref.setEmail(email);
preferenceReopsitory.save(pref);
user.setPreference(pref);
userRepository.save(user);
}
}2.3. Работа С Исключениями
И простое Исключение UserAlreadyExistsException :
public class UsernameAlreadyExistsException extends RuntimeException {
public UsernameAlreadyExistsException(String message) {
super(message);
}
public UsernameAlreadyExistsException(String message, Throwable cause) {
super(message, cause);
}
}Исключение обрабатывается в основном обработчике исключений приложения :
@ExceptionHandler({ UsernameAlreadyExistsException.class })
public ResponseEntity2.4. Простая Страница Регистрации
Наконец – то-простой интерфейс signup.html :
Стоит еще раз отметить, что это не полностью зрелый процесс регистрации – просто очень быстрый процесс. Для получения полной информации о процессе регистрации вы можете ознакомиться с основной серией регистраций здесь, на сайте Baeldung.
3. Новая Страница Входа в Систему
Вот наша новая и простая страница входа в систему :
Sign upInvalid username or password
4. Конфигурация системы Безопасности
Теперь – давайте взглянем на новую конфигурацию безопасности :
@Configuration
@EnableWebSecurity
@ComponentScan({ "org.baeldung.security" })
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.formLogin()
.loginPage("/")
.loginProcessingUrl("/j_spring_security_check")
.defaultSuccessUrl("/home")
.failureUrl("/?error=true")
.usernameParameter("username")
.passwordParameter("password")
...
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}Большинство вещей довольно просты, поэтому мы не будем подробно останавливаться на них здесь.
И вот обычай UserDetailsService :
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new UserPrincipal(user);
}
}И вот наш пользовательский Принципал ” Принципал пользователя” , который реализует Учетные записи пользователей :
public class UserPrincipal implements UserDetails {
private User user;
public UserPrincipal(User user) {
super();
this.user = user;
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}Примечание: Мы использовали наш пользовательский Принципал ” UserPrincipal” вместо Spring Security по умолчанию Пользователь .
5. Аутентифицируйте Reddit
Теперь, когда мы больше не полагаемся на Reddit для нашего потока аутентификации, нам нужно разрешить пользователям подключать свои учетные записи к Reddit после входа в систему.
Во – первых, нам нужно изменить старую логику входа в Reddit:
@RequestMapping("/redditLogin")
public String redditLogin() {
OAuth2AccessToken token = redditTemplate.getAccessToken();
service.connectReddit(redditTemplate.needsCaptcha(), token);
return "redirect:home";
}И фактическая реализация – метод connectReddit() :
@Override
public void connectReddit(boolean needsCaptcha, OAuth2AccessToken token) {
UserPrincipal userPrincipal = (UserPrincipal)
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User currentUser = userPrincipal.getUser();
currentUser.setNeedCaptcha(needsCaptcha);
currentUser.setAccessToken(token.getValue());
currentUser.setRefreshToken(token.getRefreshToken().getValue());
currentUser.setTokenExpiration(token.getExpiration());
userRepository.save(currentUser);
}Обратите внимание, как логика reddit Login() теперь используется для подключения учетной записи пользователя в нашей системе к его учетной записи Reddit путем получения Доступа пользователя .
Что касается интерфейса – это довольно просто:
Welcome, Bob
Connect your Account to Reddit
Нам также необходимо убедиться, что пользователи подключают свои учетные записи к Reddit, прежде чем пытаться отправлять сообщения:
@RequestMapping("/post")
public String showSubmissionForm(Model model) {
if (getCurrentUser().getAccessToken() == null) {
model.addAttribute("msg", "Sorry, You did not connect your account to Reddit yet");
return "submissionResponse";
}
...
}6. Заключение
Небольшое приложение reddit определенно продвигается вперед.
Старый поток аутентификации, полностью поддерживаемый Reddit, вызывал некоторые проблемы. Итак, теперь у нас есть чистый и простой логин на основе формы , при этом вы все еще можете подключить свой API Reddit в задней части.
Хорошая штука.