jpablog : login with spring security

This commit is contained in:
kim
2021-01-30 20:00:10 +09:00
parent a0bedb3791
commit 34a5f77013
7 changed files with 115 additions and 13 deletions

View File

@@ -1,5 +1,7 @@
package com.example.jpablog.config;
import com.example.jpablog.config.auth.PrincipalDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -10,22 +12,36 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration // bean 등록
@RequiredArgsConstructor
@EnableWebSecurity // 시큐리티 필터 등록
@EnableGlobalMethodSecurity(prePostEnabled = true) // 특정 주소 접근시 먼저 권한 및 인증 체크
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final PrincipalDetailService principalDetailService;
@Bean
public BCryptPasswordEncoder encodePWD() {
return new BCryptPasswordEncoder();
}
// 시큐리티가 대신 로그인 할때 어떤 해시를 사용했는지 알아야 DB와 비교가능
@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/**", "/images/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/auth/loginForm");
.csrf().disable() // csrf 토큰 비활성화
.authorizeRequests() // 요청에 대한 인증
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/images/**").permitAll() // 해당 주소로 들어오면 모두 허가
.anyRequest().authenticated() // 나머지 요청은 모두 인증을 거쳐야 함
.and()
.formLogin().loginPage("/auth/loginForm") // 로그인 폼 지정
.loginProcessingUrl("/auth/loginProc") // 해당 주소로 들어오는 로그인 요청을 가로채서 스프링 시큐리티가 로그인
.defaultSuccessUrl("/"); // 정상 로그인 완료 후 이동 URL
}
}

View File

@@ -0,0 +1,58 @@
package com.example.jpablog.config.auth;
import com.example.jpablog.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayDeque;
import java.util.Collection;
public class PrincipalDetail implements UserDetails {
private final User user;
public PrincipalDetail(User user) {
this.user = user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
// 계정 만료 여부 ( false : 만료 )
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정이 잠금 여부 ( false : 잠김 )
@Override
public boolean isAccountNonLocked() {
return true;
}
// 비밀번호 만류 여부 ( false : 만료 )
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정 활성화 여부 ( false : 비활성화 )
@Override
public boolean isEnabled() {
return true;
}
// 계정의 권한 목록 리턴
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collections = new ArrayDeque<>();
collections.add(() -> "ROLE_" + user.getRole());
return collections;
}
}

View File

@@ -0,0 +1,27 @@
package com.example.jpablog.config.auth;
import com.example.jpablog.model.User;
import com.example.jpablog.repository.UserRepository;
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
public class PrincipalDetailService implements UserDetailsService {
private final UserRepository userRepository;
// 스프링이 로그인 요청시 username, password 변수 체크
// password 는 스프링 시큐리티가 알아서 처리
// username 은 DB에 있는지 체크
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User principal = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다."));
return new PrincipalDetail(principal); // 시큐리티 세션에 유저 정보 저장
}
}

View File

@@ -1,11 +1,14 @@
package com.example.jpablog.controller;
import com.example.jpablog.config.auth.PrincipalDetail;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class BoardController {
// 파라미터로 시큐리티 세션 접근 : @AuthenticationPrincipal PrincipalDetail principal
@GetMapping("/")
public String index() {
return "index";

View File

@@ -5,10 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository // 생략가능
public interface UserRepository extends JpaRepository<User, Long> {
// select * from user where username =?
Optional<User> findByUsername(String username);

View File

@@ -22,7 +22,7 @@ spring:
jpa:
open-in-view: true
hibernate:
ddl-auto: create
ddl-auto: update
# naming:
# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
use-new-id-generator-mappings: false

View File

@@ -3,7 +3,7 @@
<%@include file="../layout/header.jsp"%>
<div class="container">
<form action="#" method="post">
<form action="/auth/loginProc" method="post">
<div class="form-group">
<label for="username">Username:</label>
<input name="username" type="text" class="form-control" placeholder="Enter username" id="username">
@@ -12,11 +12,6 @@
<label for="password">Password:</label>
<input name="password" type="password" class="form-control" placeholder="Enter password" id="password">
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input name="remember" class="form-check-input" type="checkbox"> Remember me
</label>
</div>
<button id="btn-login" type="submit" class="btn btn-primary">로그인</button>
</form>
</div>