Spring/Spring Security

[Security] 간단한 구현을 위한 정리

doriver 2022. 11. 15. 21:54

Security를 이용해 로그인/로그아웃, 권한설정 등을 처리
설정은 WebSecurityConfigurerAdapter라는 클래스를 상속받은 클래스에서 메서드를 오버라이딩하여 조정

@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    private MemberService memberService;
	
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // BCryptPasswordEncoder는 Spring Security에서 제공하는 비밀번호 암호화 객체
    } // 비밀번호를 암호화할 수 있도록 Bean으로 등록
	
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring(). ~ ~ ; // Spring Security가 무시할 수 있도록 설정
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(). ~ ~ // 페이지 권한 설정 , HttpServletRequest에 따라 접근을 제한
                    
            .and().formLogin(). ~ ~ // 로그인 설정 , form 기반으로 인증을 하도록 함( 로그인 정보는 기본적으로 HttpSession을 이용 )
                
            .and().logout(). ~ ~ // 로그아웃 설정
              
            .and().exceptionHandling(). ~ ~ ;  // 예외처리 핸들링          
    }
    
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(memberService).passwordEncoder(passwordEncoder()); // 로그인 처리(인증) 
    } 
}

위 코드에서 일부를 아래와 같이 간략화 해서 보기 편하게 해봄

void configure(WebSecurity web) {     web. ~ ~ ;    }   
// WebSecurity는 FilterChainProxy를 생성하는 필터

void configure(HttpSecurity http) {    http. ~ ~ ;    }
// HttpSecurity를 통해 HTTP 요청에 대한 웹 기반 보안을 구성

void configure(AuthenticationManagerBuilder auth) {     auth. ~ ~ ;     }
// Spring Security에서 모든 인증은 AuthenticationManager를 통해 이루어진다.

 

로그인 처리(인증)을 위해 클래스MemberService에서

인터페이스UserDetailsService를 implements하여, 메서드loadUserByUsername()를 구현

@Service
@AllArgsConstructor
public class MemberService implements UserDetailsService {
	
    private MemberRepository memberRepository;

    @Transactional
    public Long joinUser(MemberDto memberDto) { // 회원가입을 처리하는 메서드
        // 비밀번호 암호화 하여 저장
      	~ ~
        		~ ~
        return  ~ ;
    }
	
    @Override // 상세 정보를 조회하는 메서드
    public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {  
        ~ ~
				~ ~
        return new User( username, password, ~ ~ , authorities);
    }
}

SpringSecurity에서 제공하는 UserDetails인터페이스를 구현한 User(사용자의 계정정보와 권한을 담아서)를 반환

 

 


 thymeleaf-extras-springsecurity5

        사용자의 Role에따라 보이는 메뉴를 다르게 함
        -----------            
    <a sec:authorize="isAnonymous()" th:href="@{/user/login}">로그인</a>
                                      ------  
                                     해당 uri로 이동

    <a sec:authorize="isAuthenticated()" th:href="@{/user/logout}">로그아웃</a>
                   인증된 사용자에게 노출

    <a sec:authorize="isAnonymous()" th:href="@{/user/signup}">회원가입</a>
                   익명의 사용자에게 노출

    <a sec:authorize="hasRole('ROLE_MEMBER')" th:href="@{/user/info}">내정보</a>
                  특정 롤을 가진 사용자에게 노출
    <a sec:authorize="hasRole('ROLE_ADMIN')" th:href="@{/admin}">어드민</a>

 

<span sec:authentication="name"></span>님 환영합니다~
       -----------------------
          username값을 가져옴



Spring Security가 적용되면 POST 방식으로 보내는 모든 데이터는 csrf 토큰 값이 필요
토큰 값이 없는 상태에서 form 전송을 할 경우, 컨트롤러에 POST 메서드를 매핑할 수 없다는 에러가 발생

<h1>로그인</h1>

    <form action="/user/login" method="post">
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
                            form에 히든 타입으로 csrf 토큰 값을 넘겨준다
  
        <input type="text" name="username" placeholder="이메일 입력해주세요">
				로그인 시 아이디의 name 애트리뷰트 값은 username이어야 한다.
                
        <input type="password" name="password" placeholder="비밀번호">
        <button type="submit">로그인</button>
    </form>

 

th:action을 사용하면 Thymeleaf가 csrf 토큰 값을 자동으로 추가해줌
          ---------
    <form th:action="@{/user/signup}" method="post">
        <input type="text" name="email" placeholder="이메일 입력해주세요">
        <input type="password" name="password" placeholder="비밀번호">
        <button type="submit">가입하기</button>
    </form>