Предварительные условия:Базовые знания о том, как взаимодействует клиент-сервер, цикл запрос-ответ.
В этой статье я собираюсь предоставить потрясающее руководство по работе с Authentication Manager на Java.
Итак, первый вопрос, который может возникнуть в процессе, заключается в том,что такое Authentication Manager?
Authentication Manager является фундаментальной основой для процесса аутентификации Spring security. AuthenticationManager - это API, который определяет, как должны работать фильтры Spring Security.
Относительные зависимости, которые нам нужны для интеграции аутентификации с Spring boot:
Теперь мы создадим 2 файла UserDetailsServiceImpl.java и UserDetailsService.java, которые будут выполнять все операции, связанные с пользователем.
/**
Important points : UserDetailsService comes from spring security core library
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(()
-> new UsernameNotFoundException
("user Not Found with username: " + username));
return UserDetailsImpl.build(user);
}
}
//UserDetailsImpl.java
/**
The above class while returning the built UserDetails Object uses
the below referred code
*/
public class UserDetailsImpl implements UserDetails {
/**
this class uses UserDetails Interface which is available in spring
security core library
*/
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String email;
@JsonIgnore
private String password;
public UserDetailsImpl(Long id, String username,
String email, String password) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
}
public static UserDetailsImpl build(User user) {
return new UserDetailsImpl(user.getId(),
user.getUsername(), user.getEmail(),
user.getPassword());
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id);
}
@Override
public Collection<? extends GrantedAuthority>
getAuthorities() {
return null;
}
}
Теперь, когда мы уже готовы с основной функциональной частью, мы можем перейти к работе с Controller и частями Request,Response Format.
Для целей обучения мы создадим 2 отдельных Controller-файла:
1. AuthController.java
2. UserController.java
Конечные точки, которые мы будем предоставлять в AuthController, не должны быть отключены ни для какого разрешения. Но для того, чтобы UserController выполнял любой запрос и извлекал данные, он должен запрашивать учётные данные для аутентификации.
Итак, если вы внимательно следили, мы определили свойства маршрутизации в файле SecurityConfig. В нём указано, что конечная точка всегда должна быть разрешена, поскольку для входа в приложение пользователь должен пройти процесс аутентификации, верно? Таким образом, шаблон (/api/auth/**) должен быть разрешён для любого суффиксного дерева. Но для api / test, который специфичен для UserController, он всегда должен быть аутентифицирован, поскольку это то, где мы на самом деле будем пытаться извлечь или обновить любые данные, относящиеся к пользователю.
Теперь давайте подготовим Request ,Response formats.Создайте 2 файла внутри пакета RequestFormat:
1. LoginRequest.java
2. SignUpRequest.java
В этих файлах будет указано, что мы на самом деле ожидаем от пользователя в качестве входных данных, которые будут работать для их истинной аутентификации и позволят им быстро войти в приложение.
//LoginRequest.java
public class LoginRequest {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
---------------------------------------------------------------------------
//SignUpRequest.java
public class SignupRequest {
private String username;
private String email;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Таким же образом у нас будет DTO и для Response Formats. Создайте эти файлы внутри ResponseFormats:
//JwtReponse.java
/**
this file will return us the operated user object and the token associated
with it after successful login attempt.
*/
public class JwtResponse {
private String token;
private String type = "Bearer";
private Long id;
private String username;
private String email;
public JwtResponse(String accessToken,
Long id, String username, String email) {
this.token = accessToken;
this.id = id;
this.username = username;
this.email = email;
}
// ADD GETTERS AND SETTERS HERE
}
-------------------------------------------------------------------------------
//MessageReponse.java
public class MessageResponse {
private String message;
public MessageResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
AuthController.java
Он будет обрабатывать все конечные точки api тестирования, которые выполняют операции, специфичные для пользователя.Он использует JwtUtils и PasswordEncoder. PasswordEncoder помогает нам хранить пароль, закодированный с помощью стандартизированного механизма. @ CrossOrigin указывает совместное использование ресурсов из разных источников и в нём указано, что любой входящий запрос из любого источника не должен блокироваться.
UserController.Java
Здесь мы не выполняем никаких специальных операций. Мы просто хотим ограничить маршрут, чтобы он был доступен только в том случае, если пользователь успешно прошёл аутентификацию
//UserController.java
@CrossOrigin(origins = "*", maxAge = 4800)
@RestController
@RequestMapping("/api/test")
public class UserController {
@GetMapping("/all")
public MessageResponse allAccess() {
return new MessageResponse("Server is up.....");
}
@GetMapping("/greeting")
@PreAuthorize("isAuthenticated()")
public MessageResponse userAccess() {
return new MessageResponse
("Congratulations! You are an authenticated user.");
}
}
----------------------------------------------------------------------------
//AuthController.java
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserRepository userRepository;
@Autowired
PasswordEncoder encoder;
@Autowired
JwtUtils jwtUtils;
@PostMapping("/signin")
public ResponseEntity<?> authenticateuser
(@RequestBody LoginRequest loginRequest) {
org.springframework.security.core.Authentication authentication = authenticationManager.authenticate
(new UsernamePasswordAuthenticationToken
(loginRequest.getUsername(),
loginRequest.getPassword()));
SecurityContextHolder.getContext()
.setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl)
authentication.getPrincipal();
return ResponseEntity
.ok(new JwtResponse(jwt, userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail()));
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser
(@RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest
.getUsername())) {
return ResponseEntity.badRequest()
.body(new MessageResponse
("Error: username is already taken!"));
}
if (userRepository.existsByEmail
(signUpRequest.getEmail())) {
return ResponseEntity.badRequest()
.body(new MessageResponse
("Error: Email is already in use!"));
}
// Create new user account
User user = new User(signUpRequest.getUsername(),
signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()));
userRepository.save(user);
return ResponseEntity
.ok(new MessageResponse("user registered successfully!"));
}
}
Итак, всё!Теперь вы готовы протестировать данную технологию! Если вы всё будете делать шаг за шагом, у вас должно получиться!