스프링 시큐리티에서 회원의 정보를 담기 위해서 사용하는 인터페이스는 UserDetails입니다. 이 인터페이스를 직접 구현하거나 스프링 시큐리티에서 제공하는 User클래스를 사용합니다.
@Override
public UserDetails loadUserByUsername(String email) throws
UsernameNotFoundException {
Member member = memberRepository.findByEmail(email);
if(member == null) {
throw new UsernameNotFoundException(email);
}
return User.builder()
.username(member.getEmail())
.password(member.getPassword())
.roles(member.getRole().toString())
.build();
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MemberService memberService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/members/login")
.defaultSuccessUrl("/")
.usernameParameter("email")
.failureUrl("/members/login/error")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/members/logout"))
.logoutSuccessUrl("/")
;
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService)
.passwordEncoder(passwordEncoder());
}
}
Spring Security에서 인증은 AuthenticationManager를 통해 이루어지며 AuthenticationManagerBuilder가 AuthenticationManager를 생성합니다. userDetailService를 구현하고 있는 객체로 memberService를 지정해줍니다.
@GetMapping(value = "/login")
public String loginMember(){
return "/member/memberLoginForm";
}
@GetMapping(value = "/login/error")
public String loginError(Model model){
model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요");
return "/member/memberLoginForm";
}
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
@TestPropertySource(locations="classpath:application-test.yml")
class MemberControllerTest {
@Autowired
private MemberService memberService;
@Autowired
private MockMvc mockMvc;
@Autowired
PasswordEncoder passwordEncoder;
public Member createMember(String email, String password){
MemberFormDto memberFormDto = new MemberFormDto();
memberFormDto.setEmail(email);
memberFormDto.setName("홍길동");
memberFormDto.setAddress("서울시 마포구 합정동");
memberFormDto.setPassword(password);
Member member = Member.createMember(memberFormDto, passwordEncoder);
return memberService.saveMember(member);
}
@Test
@DisplayName("로그인 성공 테스트")
public void loginSuccessTest() throws Exception{
String email = "test@email.com";
String password = "1234";
this.createMember(email, password);
mockMvc.perform(formLogin().userParameter("email")
.loginProcessingUrl("/members/login")
.user(email).password(password))
.andExpect(SecurityMockMvcResultMatchers.authenticated());
}
@Test
@DisplayName("로그인 실패 테스트")
public void loginFailTest() throws Exception{
String email = "test@email.com";
String password = "1234";
this.createMember(email, password);
mockMvc.perform(formLogin().userParameter("email")
.loginProcessingUrl("/members/login")
.user(email).password("12345"))
.andExpect(SecurityMockMvcResultMatchers.unauthenticated());
}
}
<sec:authorize="isAnonymous()">
...
<sec:authorize="isAuthenticated()">
...
<sec:authorize="hasAnyAuthority('ROLE_ADMIN')">
표현식 | 설명 |
---|---|
hasRole('role') | 해당 권한이 있을 경우 |
hasAnyRole('role1,'role2') | 포함된 권한 중 하나라도 있을 경우 |
isAuthenticated() | 권한에 관계없이 로그인 인증을 받은 경우 |
isFullyAuthenticated() | 권한에 관계없이 인증에 성공했고, 자동 로그인이 비활성인 경우 |
isAnonymous() | 권한이 없는 익명의 사용자일 경우 |
isRememberMe() | 자동 로그인을 사용하는 경우 |
permitAll | 모든 경우 출력함 |
denyAll | 모든 경우 출력하지 않음 |