jpablog : login with spring security
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package com.example.jpablog.config;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
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;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
@Configuration // bean 등록
|
@Configuration // bean 등록
|
||||||
|
@RequiredArgsConstructor
|
||||||
@EnableWebSecurity // 시큐리티 필터 등록
|
@EnableWebSecurity // 시큐리티 필터 등록
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true) // 특정 주소 접근시 먼저 권한 및 인증 체크
|
@EnableGlobalMethodSecurity(prePostEnabled = true) // 특정 주소 접근시 먼저 권한 및 인증 체크
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
private final PrincipalDetailService principalDetailService;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BCryptPasswordEncoder encodePWD() {
|
public BCryptPasswordEncoder encodePWD() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 시큐리티가 대신 로그인 할때 어떤 해시를 사용했는지 알아야 DB와 비교가능
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.csrf().disable() // csrf 토큰 비활성화
|
.csrf().disable() // csrf 토큰 비활성화
|
||||||
.authorizeRequests()
|
.authorizeRequests() // 요청에 대한 인증
|
||||||
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/images/**").permitAll()
|
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/images/**").permitAll() // 해당 주소로 들어오면 모두 허가
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated() // 나머지 요청은 모두 인증을 거쳐야 함
|
||||||
.and().formLogin().loginPage("/auth/loginForm");
|
.and()
|
||||||
|
.formLogin().loginPage("/auth/loginForm") // 로그인 폼 지정
|
||||||
|
.loginProcessingUrl("/auth/loginProc") // 해당 주소로 들어오는 로그인 요청을 가로채서 스프링 시큐리티가 로그인
|
||||||
|
.defaultSuccessUrl("/"); // 정상 로그인 완료 후 이동 URL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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); // 시큐리티 세션에 유저 정보 저장
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.example.jpablog.controller;
|
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.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class BoardController {
|
public class BoardController {
|
||||||
|
|
||||||
|
// 파라미터로 시큐리티 세션 접근 : @AuthenticationPrincipal PrincipalDetail principal
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index() {
|
public String index() {
|
||||||
return "index";
|
return "index";
|
||||||
|
|||||||
@@ -5,10 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository // 생략가능
|
@Repository // 생략가능
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
|
||||||
|
// select * from user where username =?
|
||||||
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ spring:
|
|||||||
jpa:
|
jpa:
|
||||||
open-in-view: true
|
open-in-view: true
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: create
|
ddl-auto: update
|
||||||
# naming:
|
# naming:
|
||||||
# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
||||||
use-new-id-generator-mappings: false
|
use-new-id-generator-mappings: false
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<%@include file="../layout/header.jsp"%>
|
<%@include file="../layout/header.jsp"%>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form action="#" method="post">
|
<form action="/auth/loginProc" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input name="username" type="text" class="form-control" placeholder="Enter username" id="username">
|
<input name="username" type="text" class="form-control" placeholder="Enter username" id="username">
|
||||||
@@ -12,11 +12,6 @@
|
|||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input name="password" type="password" class="form-control" placeholder="Enter password" id="password">
|
<input name="password" type="password" class="form-control" placeholder="Enter password" id="password">
|
||||||
</div>
|
</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>
|
<button id="btn-login" type="submit" class="btn btn-primary">로그인</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user