fastcampus 웹 개발 마스터 초격차 패키지를 수강하며 정리한 내용들입니다.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student {
private String id;
private String username;
private Set<GrantedAuthority> role;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class StudentAuthenticationToken implements Authentication {
// student가 받게 될 통행증
private Student principal; //인증된 결과, 인증 대상
private String credentials; //인증 받기 위해 필요한 정보
private String details;
private boolean authenticated;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return principal == null ? new HashSet<>() : principal.getRole();
}
@Override
public String getName() {
return principal == null ? "": principal.getUsername();
}
}
@Component
public class StudentManager implements AuthenticationProvider, InitializingBean {
// 통행증을 발급할 provider manager
// 이러한 provider manager는 config 파일에서 등록해야 사용할 수 있다.
private HashMap<String, Student> studentDB = new HashMap<>();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// authentication으로 입력된 토큰을 UsernamePasswordAuthenticationToken로 형변환 해준다.
// 그래야 student manager가 student 통행증을 발급해주기 때문에
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
if(studentDB.containsKey(token.getName())){
Student student = studentDB.get(token.getName());
return StudentAuthenticationToken.builder()
.principal(student)
.details(student.getUsername())
.authenticated(true)
.build();
}
return null;
// 가져올 토큰값이 없다면 리턴 값을 null로 줘야한다.
// false로 주면 인증에 대해 임의의 핸들링을 한 것이기 때문이다.
}
@Override
public boolean supports(Class<?> authentication) {
// UsernamePasswordAuthenticationToken을 받으면 검증을 해주는 provider manager로 동작
return authentication == UsernamePasswordAuthenticationToken.class;
}
@Override
public void afterPropertiesSet() throws Exception {
// Set.of는 Java8 이후에 나온 기능이라 자바 버전을 변경해야한다.
Set.of(
new Student("hong", "홍길동", Set.of(new SimpleGrantedAuthority("ROLE_STUDENT"))),
new Student("kang", "강아지", Set.of(new SimpleGrantedAuthority("ROLE_STUDENT"))),
new Student("rang", "호랑이", Set.of(new SimpleGrantedAuthority("ROLE_STUDENT")))
).forEach(s->
studentDB.put(s.getId(), s)
);
}
}
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final StudentManager studentManager;
public SecurityConfig(StudentManager studentManager, TeacherManager teacherManager) {
this.studentManager = studentManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(studentManager);
auth.authenticationProvider(teacherManager);
// providermanager로 StudentManager를 사용하도록 설정
}
@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")
)
.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 class CustomLoginFilter extends UsernamePasswordAuthenticationFilter {
// UsernamePasswordAuthenticationFilter를 직접 custom
public CustomLoginFilter(AuthenticationManager authenticationManager){
super(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = obtainUsername(request);
username = (username != null) ? username : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
-config/SecurityConfig
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomLoginFilter filter = new CustomLoginFilter(authenticationManager());
http
.authorizeRequests(request->
request.antMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
)
// 기존 코드에서 .formlogin부분을 지우고 작성
.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class)
.logout(logout-> logout.logoutSuccessUrl("/"))
.exceptionHandling(e->e.accessDeniedPage("/access-denied"))
;
}