Enabled Basic HTTP Authentication in Spring Security
This commit is contained in:
parent
b8a5f8d717
commit
14ff4b5b06
@ -0,0 +1,8 @@
|
||||
package ru.dragonestia.picker.api.impl.exception;
|
||||
|
||||
public class NotEnoughPermissions extends RuntimeException {
|
||||
|
||||
public NotEnoughPermissions(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.dragonestia.picker.api.impl.repository;
|
||||
|
||||
public class AuthException extends RuntimeException {
|
||||
|
||||
public AuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.*;
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
import ru.dragonestia.picker.api.impl.exception.NotEnoughPermissions;
|
||||
import ru.dragonestia.picker.api.impl.repository.AuthException;
|
||||
import ru.dragonestia.picker.api.exception.ExceptionFactory;
|
||||
import ru.dragonestia.picker.api.impl.RoomPickerClient;
|
||||
import ru.dragonestia.picker.api.impl.util.type.HttpMethod;
|
||||
@ -110,6 +112,13 @@ public class RestTemplate {
|
||||
var statusCode = code / 100;
|
||||
|
||||
if (statusCode == 4) {
|
||||
if (code == 401) {
|
||||
throw new AuthException("Invalid username and password");
|
||||
}
|
||||
if (code == 403) {
|
||||
throw new NotEnoughPermissions("Not enough permissions");
|
||||
}
|
||||
|
||||
var body = new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8);
|
||||
throw ExceptionFactory.of(json.readValue(body, ErrorResponse.class));
|
||||
}
|
||||
|
||||
@ -20,6 +20,6 @@ public class RoomPickerConfig {
|
||||
|
||||
@Bean
|
||||
RoomPickerClient roomPickerClient() {
|
||||
return new RoomPickerClient(serverUrl, "test", "test");
|
||||
return new RoomPickerClient(serverUrl, "admin", "qwerty123");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,25 +3,54 @@ package ru.dragonestia.picker.config;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new PasswordEncoder() { // TODO: use hash algorithm
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return rawPassword.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return rawPassword.toString().equals(encodedPassword);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
|
||||
http.csrf(AbstractHttpConfigurer::disable);
|
||||
http.logout(AbstractHttpConfigurer::disable);
|
||||
http.formLogin(AbstractHttpConfigurer::disable);
|
||||
http.sessionManagement(m -> m.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
http.authorizeHttpRequests(auth -> {
|
||||
auth
|
||||
.requestMatchers("/actuator").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
.requestMatchers("/api-docs-ui").permitAll()
|
||||
.requestMatchers("/swagger-ui").permitAll()
|
||||
.requestMatchers("/swagger-ui/**").permitAll()
|
||||
.requestMatchers("/info").permitAll()
|
||||
.anyRequest().authenticated();
|
||||
});
|
||||
http.httpBasic(Customizer.withDefaults());
|
||||
http.userDetailsService(userDetailsService);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
package ru.dragonestia.picker.model;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Account implements UserDetails {
|
||||
|
||||
private final String username;
|
||||
private final String lowerUsername;
|
||||
private String password;
|
||||
private Set<Permission> permissions = new HashSet<>();
|
||||
private boolean locked = false;
|
||||
private boolean enabled = true;
|
||||
|
||||
public Account(@NotNull String username, @NotNull String password) {
|
||||
this.username = username;
|
||||
this.lowerUsername = username.toLowerCase();
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Permission> getAuthorities() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull Account setAuthorities(@NotNull Set<Permission> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull Account setPassword(String value) {
|
||||
password = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return !locked;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull Account setLocked(boolean value) {
|
||||
locked = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull Account setEnabled(boolean value) {
|
||||
enabled = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return lowerUsername.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (obj == this) return true;
|
||||
if (obj instanceof Account other) {
|
||||
return lowerUsername.equals(other.lowerUsername);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package ru.dragonestia.picker.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum Permission implements GrantedAuthority {
|
||||
ADMIN("admin");
|
||||
|
||||
private final String id;
|
||||
|
||||
Permission(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package ru.dragonestia.picker.service;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import ru.dragonestia.picker.model.Account;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface AccountService extends UserDetailsService {
|
||||
|
||||
@NotNull Account createNewAccount(@NotNull String username, @NotNull String password);
|
||||
|
||||
@NotNull Collection<Account> allAccounts();
|
||||
|
||||
void removeAccount(@NotNull Account account);
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package ru.dragonestia.picker.service.impl;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.dragonestia.picker.model.Account;
|
||||
import ru.dragonestia.picker.model.Permission;
|
||||
import ru.dragonestia.picker.service.AccountService;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccountServiceImpl implements AccountService {
|
||||
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
private final Map<String, Account> accounts = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
var account = createNewAccount("admin", "qwerty123");
|
||||
account.setAuthorities(Set.of(Permission.ADMIN));
|
||||
}
|
||||
|
||||
public @NotNull Account createNewAccount(@NotNull String username, @NotNull String password) {
|
||||
var account = new Account(username, passwordEncoder.encode(password));
|
||||
accounts.put(account.getUsername().toLowerCase(), account);
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Account> allAccounts() {
|
||||
return accounts.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAccount(@NotNull Account account) {
|
||||
accounts.remove(account.getUsername());
|
||||
account.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
var lowerUsername = username.toLowerCase();
|
||||
if (accounts.containsKey(lowerUsername)) {
|
||||
return accounts.get(lowerUsername);
|
||||
}
|
||||
|
||||
throw new UsernameNotFoundException("User '" + username + "' does not exists");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user