여러가지 로그인 방식
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
username = (username != null) ? username : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
// username,passwd로 통행증 만들기
// 원래 UsernamePasswordAuthenticationToken 안에 authenticated = false임
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
// 인증토큰을 가지고 manager에게 내 인증을 처리해달라고 요청함
// 연결되어 있는 authenticationProvider들에게 물어보고 누구든지 처리해주면 인증완료!
return this.getAuthenticationManager().authenticate(authRequest);
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Logging out [%s]", auth));
}
this.handler.logout(request, response, auth);
this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
return;
}
chain.doFilter(request, response);
}
기본 index.html의 설정을 해놓는 코드이다. url에 따라 권한을 설정하기도 하고 login 성공/실패에 따라 동작하는 방식을 정하기도 한다. 또한 CustomAuthDetails 객체를 만들어서 detail한 정보를 직접 볼 수 있도록 세팅해놓을 수 있다.
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthDetails customAuthDetails;
public SecurityConfig(CustomAuthDetails customAuthDetails) {
this.customAuthDetails = customAuthDetails;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication() // 해당 설정을 하게되면 .yaml의 user는 더이상 접속 불가
.withUser(User.withDefaultPasswordEncoder()
.username("user1")
.password("1111")
.roles("USER")
)
.withUser(User.withDefaultPasswordEncoder()
.username("admin")
.password("2222")
.roles("ADMIN")
);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(request->{
request
.antMatchers("/").permitAll() // 메인화면은 권한 필요x
.anyRequest().authenticated();
})
.formLogin(// 기본 로그인/로그아웃 화면 나오게 해줌
login->login.loginPage("/login")
.permitAll() // 로그인화면을 만든 것으로 바꿈
.defaultSuccessUrl("/",false) // 로그인하고 메인 페이지로 안가겠다
.failureUrl("/login-error")
.authenticationDetailsSource(customAuthDetails) // 필요에 따라서 유용하게 쓰일 수 있음
)
.logout(logout -> logout.logoutSuccessUrl("/"))
.exceptionHandling(exception -> exception.accessDeniedPage("/access-denied"))
;
}
@Bean
RoleHierarchy roleHierarchy(){ // admin은 유저가 할 수 있는 기능을 다 할 수 있음
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return roleHierarchy;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.requestMatchers( // resource 파일들은 일단 적용되게 해주는 메서드
PathRequest.toStaticResources().atCommonLocations()
);
}
}
정말 말 그대로 기초 로그인인데 내용이 이렇게 어려운거봐서는 역시 spring이 쉽지 않다...
Authentication 메커니즘
@Component
public class TeacherManager implements AuthenticationProvider, InitializingBean {
// 통행증을 다룰 객체(통행증을 발급해주는 역할)
private HashMap<String, Teacher> teacherDB = new HashMap<>();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
TeacherAuthenticationToken token = (TeacherAuthenticationToken) authentication;
if(teacherDB.containsKey(token.getCredentials())){
Teacher teacher = teacherDB.get(token.getCredentials());
return TeacherAuthenticationToken.builder()
.principal(teacher)
.details(teacher.getUsername())
.authenticated(true)
.build();
}
// 처리할 수 없는 token을 false로해서 넘기면 handling했다는 것이 때문에 문제가 됨
return null; // 처리할 수 없는 Authentication은 null로 넘김
}
@Override
public boolean supports(Class<?> authentication) {
// TeacherAuthenticationToken가 Token을 발행해서 인증을 해줄 provider를 찾는데
// authentication이 그 토큰이라면?
// 해당 메서드로 provider로 동작을 하겠다!라고 선언
// 즉 인증을 위임하겠다!
return authentication == TeacherAuthenticationToken.class;
}
@Override
public void afterPropertiesSet() throws Exception {
Set.of(
new Teacher("ryuT","류선생", Set.of(new SimpleGrantedAuthority("ROLE_TEACHER"))),
new Teacher("leeT","이선생", Set.of(new SimpleGrantedAuthority("ROLE_TEACHER"))),
new Teacher("parkT","박선생", Set.of(new SimpleGrantedAuthority("ROLE_TEACHER")))
).forEach(t ->
teacherDB.put(t.getId(),t));
}
}
출처 : 한 번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online.