스프링시큐리티와 관련된 어댑터 클래스를 오버라이드하여 사용하는 방법에서 컴포넌트 방식의 빈을 설정하는 방식으로 사용방식이 변경되었다.
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/me").hasAnyRole("USER","ADMIN")
.anyRequest().permitAll()
.and()
.formLogin()
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(300);
}
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/me").hasAnyRole("USER","ADMIN")
.anyRequest().permitAll()
.and()
.formLogin()
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(300);
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/assets/**", "/h2-console/**");
}
}
UserDetailsService의 구현체인 JdbcDaoImpl 클래스를 설정을 통해 기존 데이터베이스를 활용한 인증 확인 기능을 더욱 정교하게 설정하는 것이 가능하다. 대표적인 사용 예시는 사용자가 속하는 특정 그룹이 가지고 있는 권한 집합을 참조하는 방식의 Group-based Access Control의 적용이다.
select username, password, enabled from users where username = ?
select username, authority from authorities where username = ?
select g.id, g.group_name, ga.authority
from groups g, group_members gm, group_authorities ga
where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
AuthenticationManagerBuilder를 통해 수정된 쿼리와 권한 부여 방식을 수정한다.
@Bean
public UserDetailsService userDetailsService(DataSource dataSource) {
JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
jdbcDao.setDataSource(dataSource);
jdbcDao.setEnableAuthorities(false);
jdbcDao.setEnableGroups(true);
jdbcDao.setUsersByUsernameQuery(
"select login_id, passwd, true " +
"from users " +
"where login_id = ?"
);
jdbcDao.setGroupAuthoritiesByUsernameQuery(
"select u.login_id, g.name, p.name \n" +
"from users u join groups g on u.group_id = g.id\n" +
"left join groups_permission gp on g.id = gp.group_id\n" +
"join permissions p on p.id = gp.permission_id\n" +
"where u.login_id = ?"
);
return jdbcDao;
}
인증과 관련된 엔티티를 설계한다.
사용자 조회와 관련된 JpaRepository인터페이스 메소드를 추가한다.
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByLoginId(String loginId);
}
2의 메소드를 활용하여 UserDetailsService 인터페이스의 loadUserByUsername 메소드를 오버라이딩 한다.(반환되는 UserDetails 객체는 Authentication 객체로 캡슐화 된다.)
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByLoginId(username)
.map(user ->
User.builder()
.username(user.getLoginId())
.password(user.getPasswd())
.authorities(user.getGroup().getAuthorities())
.build()
)
.orElseThrow(() -> new UsernameNotFoundException("Could not found user for " + username));
}
}
3을 UserDetailsSerivice 인터페이스의 구현체로 사용할 수 있도록 AuthenticationManagerBuilder의 값을 수정한다.
@Configuration
@EnableWebSecurity
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {
private final Logger log = LoggerFactory.getLogger(getClass());
private UserService userService;
@Autowired
private void setUserService(UserService userService) {
this.userService = userService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
}
@Configuration
@EnableWebSecurity
public class WebSecurityConfigure {
@Bean
@Primary
public AuthenticationManagerBuilder myAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor, UserService userService) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
authenticationManagerBuilder.userDetailsService(userService);
return authenticationManagerBuilder;
}
}