사실 스프링 시작 하는 사람들에게 있어서 가장 어려운 부분이 아닐 까 생각 된다.
보안이라는게 사실 타협 할 수 없는 부분이고 그래서 스프링 시큐리티를 잘 이해 하고 사용 해야 한다.
그래서 이번에는 스프링 시큐리티를 따라잡아 보려고 한다.
이게 사실 스프링을 시작한지 얼마 안된 사람들에게 가장 큰 벽이 아닐까...
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.1.0'
SSR 설정을 위해서 thymeleaf 설정과 security 설정을 해준다.
User extends UserDetails
UserRepository extends JpaRepository
UserService extends UserDetailsService
SecurityConfig extends WebSecurityConfigurerAdapter
SecurityController extends Controller
public class User implements UserDetails {
private String username;
private String password;
private String email;
private String name;
private String phone;
private String address;
private String role;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private Collection<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
// return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
// return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
// return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"), new SimpleGrantedAuthority("ROLE_ADMIN"));
// return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"), new SimpleGrantedAuthority("ROLE_ADMIN"), new SimpleGrantedAuthority("ROLE_MANAGER"));
// return createAuthorities(List.of("ROLE_USER", "ROLE_ADMIN", "ROLE_MANAGER"));
}
/* public List<GrantedAuthority> createAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
return authorities;
}*/
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
권한이 여러개인 경우... authorities를 List로 만들어서 넣어주면 된다.
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
import java.beans.BeanProperty;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/signup").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
}
}
위처럼 configuration 을 해줘도 되고 아래와 같이 설정도 가능하다.
위는 스프링 security에 관한 filter 만을 사용하고 아래는 스프링 security에 관한 filter 와 기존의 filter 를 사용한다.
@Configuration
public class SecurityConfiguration{
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/signup").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
return http.build();
}
}
사실 이렇게 설정을 해주면 끝이다.
음 security user 정보를 사용하기 위한 방법이 정말 많은데 이번에는 userDetails 를 사용 했다.
위 5가지 클래스만 잘 만들어주면 된다.
사실 이렇게 하면 권한이 하나인 경우에는 괜찮지만 권한이 여러개인 경우에는 좀 더 코드를 이쁘게 짜야할 것 같다.