반응형
안녕하세요.
Spring Security에 대해서 공부하고 알게되는 내용에 대해서 정리해보려고 합니다.
스스로 간단하게 정리를 하기에 주석에다가 정리를 하였고, 공부하였으니 보기 불편하실 수도 있습니다.
이 포스팅은 아래의 강의를 참고하였으니 여기에서 공부하시는 것을 추천드립니다.
들어가기 앞서, 프로젝트 개발 환경(필자 기준)
- 인텔리제이
- H2 DB
- Spring Boot 2.7.0
- maven project
추가한 라이브러리
- spring-data-jpa : jpa를 편하게 사용하기 위해
- mustache : 정적 템플릿 사용을 도와줌
- spring security : 이번 학습 목표. Spring security
- spring-boot-devtools : Spring 에서 개발을 편하게 해주는 라이브러리
- h2database : H2 DB와 연결 할 수 있게 도와주는 라이브러리
- spring-boot-web : Web으로 개발 할 수 있게 도와주는 라이브러리
- lombok : getter, setter, toString 등 엔티티를 편하게 작성할 수 있게 도와주는 라이브러리
pom.xml 일부 (참고)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
스프링 시큐리티 설정
WebSecurityConfigurerAdapter를 상속받아서 설정 파일을 생성하기
@Configuration // 설정과 관련된 클래스파일임을 알리는 어노테이션
@EnableWebSecurity // spring Security filter가 Spring filter chain 에 등록
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
계정 생성, 로그인, 권한 처리 방법
1. SecurityConfig 파일(WebSecurityConfigurerAdapter을 상속받은 설정 파일)
package com.cos.security1.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
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;
@Configuration
@EnableWebSecurity // spring security filter가 Spring filter chain 에 등록
// securedEnabled: @Secured 어노테이션 활성화. @Secured? 특정 URL에 대해서만 간단하게 권한 처리를 할 수 있는 어노테이션
// prePostEnabled: @PreAuthorize, @PostAuthorize 어노테이션 활성화.
// @PreAuthorize: 해당 메소드 진입 전 처리. @PostAuthorize: 해당 메소드 진입 후 처리
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* password 암호화 메소드. 추후 계정 생성 시, 사용함
* @return
*/
@Bean
public BCryptPasswordEncoder encodePw(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception{ // Spring Security 설정
http.csrf().disable(); // csrf 비활성화
// URL에 따른 접근 제한 처리
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // URL user : 인증이 되어야 함
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')") // URL manager : 권한 'ROLE_ADMIN', 'ROLE_MANAGER'가 있어야함
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // URL admin : 권한 'ROLE_ADMIN'이 있어야함
.anyRequest().permitAll() // 위에 명시되지 않은 URL 은 로그인 및 권한 검사 X
.and()
.formLogin()
.loginPage("/loginForm") // formLogin이 필요한 경우, /login 으로 보낸다.
.loginProcessingUrl("/login") // login 주소가 호출이 되면 시큐리티가 낚이채서 대신 로그인을 진행
.defaultSuccessUrl("/"); // login 성공 시, 보내줄 기본 url
}
}
2. User Entity
JPA에 대해 생소하신 분은 이해가 안가실 수 있으니, 위에 남긴 강의를 직접 들어보시는 것을 추천드립니다.
package com.cos.security1.model;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.sql.Timestamp;
@Entity(name="tb_user")
@Data
public class User {
@Id @GeneratedValue
private Long id;
private String username;
private String password;
private String email;
private String role;
@CreationTimestamp
private Timestamp createDate;
}
3. PrincipalDetails
package com.cos.security1.config.auth;
// 시큐리티가 /login url을 낚아채서 로그인을 진행시킴.
// 로그인을 하면 security 전용 session을 만들어준다. (Security ContextHolder)
// 오브젝트 타입 => Authentication 타입 객체
// Authentication 안에 User 정보가 있어야 한다.
// User 오브젝트 타입 => UserDetails 타입 객체
import com.cos.security1.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
// Security Session 안에 들어갈 수 있는 객체: Authentication
// Authentication 안에 들어갈 수 있는 객체: UserDetails
public class PrincipalDetails implements UserDetails {
private User user;
public PrincipalDetails(User user){
this.user = user;
}
// 해당 유저의 권한을 리턴하는 method
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
String role = user.getRole();
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return role;
}
});
return collect;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
// 만료 여부
public boolean isAccountNonExpired() {
return true;
}
@Override
// 잠금 여부
public boolean isAccountNonLocked() {
return true;
}
@Override
// 만료 여부
public boolean isCredentialsNonExpired() {
return true;
}
@Override
// 회원 사용 여부
public boolean isEnabled() {
return true;
}
}
4. PrincipalDetailsService
package com.cos.security1.config.auth;
import com.cos.security1.Repository.UserRepository;
import com.cos.security1.model.User;
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;
// Security 설정에서 loginProcessingUrl("/login")으로 걸었기 때문에
// "/login" 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername 메소드가 실행된다.
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// Security Session = Authentication
// Authentication => UserDetails
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println(username);
User userEntity = userRepository.findByUsername(username);
if(userEntity != null){
return new PrincipalDetails(userEntity);
}
return null;
}
}
5. IndexController(MainController)
package com.cos.security1.controller;
import com.cos.security1.Repository.UserRepository;
import com.cos.security1.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@Autowired
UserRepository userRepository;
@Autowired
BCryptPasswordEncoder encoder;
...
// 회원가입 로직
@PostMapping({"/join"})
public String join(User user){
user.setRole("ROLE_ADMIN");
// 패스워드 암호화를 진행하지 않으면 시큐리티 로그인이 불가능
String encPw = encoder.encode(user.getPassword());
user.setPassword(encPw);
userRepository.save(user);
return "redirect:/loginForm";
}
@GetMapping({"/joinProc"})
@ResponseBody
public String joinProc(){
return "회원가입 완료";
}
@Secured("ROLE_ADMIN") // 이 메소드에 대해서만 특정 권한이 필요할 때 사용 가능
@GetMapping("/info")
@ResponseBody
public String info(){
return "개인정보";
}
@PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')") // 메소드 접근 이전에 확인하는 로직
//@PostAuthorize() // 메소드 접근 이후에 확인하는 로직
@GetMapping("/data")
@ResponseBody
public String data(){
return "데이터정보";
}
}
반응형
'BE > Spring' 카테고리의 다른 글
[Spring Security] 페이스북 로그인하기 (0) | 2022.06.03 |
---|---|
[Spring Security] oAuth2.0 - 구글로 로그인하기 (0) | 2022.06.02 |
[스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술] - 1일차 (0) | 2022.05.29 |
[Spring Boot] 이메일 전송하는 방법 (0) | 2022.05.16 |
[Spring Boot] MongoDB 통신 설정 (0) | 2022.05.14 |