웹사이트, 모바일 서비스 개발(두개의 정책을 사용해야 할 시)
// 모바일쪽 - CSRF disable & BasicAuthenticationFilter 이용
@Order(1)
@Configuration // EnableWebSecurity 와 EnableGlobalMethodSecurity는 중복 해서쓰면안됨
public class MobileSecurityConfig extends WebSecurityConfigurerAdapter {
private final StudentManager studentManager;
private final TeacherManager teacherManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomLoginFilter filter = new CustomLoginFilter(authenticationManager());
http
.antMatcher("/api/**")
.csrf().disable()
.authorizeRequests(request->
request.anyRequest().authenticated()
)
.httpBasic()
;
}
}
// 웹사이트 쪽 - csrf 이용
@Order(2)
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomLoginFilter filter = new CustomLoginFilter(authenticationManager());
http
.authorizeRequests(request->
request.antMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
)
// .formLogin(login -> login.loginPage("/login").permitAll()
// .defaultSuccessUrl("/", false)
// .failureUrl("/login-error"))
.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class)
.logout(logout -> logout.logoutSuccessUrl("/"))
.exceptionHandling(e -> e.accessDeniedPage("/access-denied"))
;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
;
}
}
웹 쪽 개발 예시
public String main(@AuthenticationPrincipal Teacher teacher, Model model){
model.addAttribute("studentList", studentManager.myStudentList(teacher.getId()));
return "TeacherMain";
}
- model.addAttribute는 template에서 해당 attribute를 사용할 수 있도록 해줌
API 측
@PreAuthorize("hasAnyAuthority('ROLE_TEACHER')")
@GetMapping("/students")
public List<Student> main(@AuthenticationPrincipal Teacher teacher){
return studentManager.myStudentList(teacher.getId());
}
- @AuthenticationPrincipal을 이용하여 authenticate 된 Authentication의 Principal을 얻어와서 인자로 사용함
테스트 코드
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MultiChainProxyTest {
@LocalServerPort
int port;
RestTemplate restTemplate = new RestTemplate();
@DisplayName("1. 학생 조사")
@Test
void test_1() throws JsonProcessingException {
String url = String.format("http://localhost:%d/api/teacher/students", port);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.AUTHORIZATION, "BASIC " + Base64.getEncoder().encodeToString(
"choi:1".getBytes()
));
HttpEntity<String> entity = new HttpEntity<>("", httpHeaders);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
List<Student> list = new ObjectMapper().readValue(
response.getBody(), new TypeReference<List<Student>>() {
});
System.out.println(list);
Assertions.assertEquals(3, list.size());
}
}
- TypeReference 는 Jackson(Json타입), ParameterizedTypeReference는 RestTemplate에서 쓰임