loginForm.jsp 수정
<form action="/auth/loginProc" method="post">
이것저것 새로 만들어서 폴더트리 첨부.
스프링 시큐리티는 유저 정보가 UserDetails 라는 인터페이스에 담김.
UserDetails를 구현하는 PrincipalDetial.java를 만들 것임.
PrincipalDetail 안에 User user를 인자로 가진 생성자를 만들어서 UserDetails의 메서드를 오버라이딩함.
package com.pure.blog.config.auth;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.pure.blog.model.User;
//스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행한 후 완료가 되면 UserDetails 타입의 오브젝트를 스프링 시큐리티의 세션에 저장해줌.
public class PrincipalDetail implements UserDetails {
private User user;
public PrincipalDetail(User user) {
this.user = user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
//계정이 만료되지 않았는지를 리턴 (true: 아직 유효)
@Override
public boolean isAccountNonExpired() {
return true;
}
//계정이 잠겨있지 않은지 리턴 (ture: 잠겨있지 않음)
@Override
public boolean isAccountNonLocked() {
return true;
}
//비밀변호가 만료되지 않았는지 리턴 (true: 아직 유효)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//계정 활성화 상태인지 리턴 (true: 활성화)
@Override
public boolean isEnabled() {
return true;
}
//계정이 갖고 있는 권한 목록을 리턴함. 원래 권한이 여러개 있을 수 있어서 루프를 돌려야 하지만 지금은 권한이 USER 하나밖에 없다.
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collectors = new ArrayList<>();
/*
* collectors.add(new GrantedAuthority() {
*
* @Override public String getAuthority() { return "ROLE_"+user.getRole();
* //ROLE_USER 스프링 시큐리티에서는 role 앞에 "ROLE_"을 붙여주어야 인식. } });
*/
// add() 안에는 GrantedAuthority 밖에 못들어 가고 GrantedAuthority 안에는 메서드가 하나밖에 없기 때문에 람다식으로 처리
collectors.add(() -> {return "ROLE_"+user.getRole();});
return collectors;
}
}
스프링 시큐리티는 비밀번호는 알아서 처리해줌. username이 DB에 있는지 확인하기 위한 PrincipalDetailService.java 작성.
package com.pure.blog.config.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.pure.blog.model.User;
import com.pure.blog.repository.UserRepository;
@Service
public class PrincipalDetailService implements UserDetailsService{
@Autowired
private UserRepository userRepository;
//스프링이 로그인 요청을 처리할 때 password는 알아서 처리해줌. 그래서 username만 DB에 있는지 확인시켜 주면 됨.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User principal = userRepository.findByUsername(username).orElseThrow( () -> {
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. :" + username);
});
return new PrincipalDetail(principal); //시큐리티의 세션에 유저 정보가 저장됨.
}
}
UserRepository에 findByUsername 메서드 작성.
package com.pure.blog.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.pure.blog.model.User;
//DAO와 같음
//자동으로 Bean 등록이 되기 때문에 @Repository 생략이 가능.
public interface UserRepository extends JpaRepository<User, Integer> { //User 테이블이 관리하는 repo, PK는 Integer
// select * from user where username = 1?;
Optional<User> findByUsername(String username);
}
header.jsp 에 잘못적은 거 있어서 수정.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authorize access="isAuthenticated()" >
<sec:authentication property="principal" var="principal"/>
</sec:authorize>
SecurityConfig.java에 로그인 시 비밀번호 해시화 방법을 말려주는 메서드 작성.
package com.pure.blog.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.pure.blog.config.auth.PrincipalDetailService;
//이 세가지는 세트 상품이라고 생각하면 편함.
@Configuration // 빈 등록
@EnableWebSecurity // 시큐리티 필터 추가 == 시큐리티 설정을 이 클래스에서 하겠다.
@EnableGlobalMethodSecurity(prePostEnabled = true) //특정 주소로 접근하면 권한 및 인증을 미리 체크함.
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private PrincipalDetailService principalDetailService;
@Bean
public BCryptPasswordEncoder encodePWD() {
return new BCryptPasswordEncoder();
}
// 시큐리티의 로그인 동작시 받은 rawpassword가 무엇으로 해쉬화 되었는지 알려주는 작업.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() //csrf 토큰 비활성화 (테스트시 설정해 두는 것이 좋음)
.authorizeRequests()
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc") //스프링 시큐리티가 해당 주소로 오는 로그인 요청을 가로채서 대신 로그인 해줌.
.defaultSuccessUrl("/");
}
}
BoardController 에서 principal 객체 확인하기
package com.pure.blog.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import com.pure.blog.config.auth.PrincipalDetail;
@Controller
public class BoardController {
@GetMapping({"","/"})
public String index(@AuthenticationPrincipal PrincipalDetail principal) {
System.out.println("로그인 아이디: "+principal.getUsername());
return "index";
}
}
어렵네... 후... 한 번 더 해보면 익숙해지겠지~
'취업 준비 > Spring boot' 카테고리의 다른 글
31. 글 목록 출력, 페이징 처리 (0) | 2022.01.27 |
---|---|
30. 글쓰기 구현 (0) | 2022.01.27 |
28. XSS와 CSRF (0) | 2022.01.26 |
27. 비밀번호 해쉬 후 회원가입하기 (0) | 2022.01.26 |
26. 스프링 시큐리티 로그인 페이지 커스터마이징 (0) | 2022.01.26 |