Spring Security 1장

간단 구현

최근 스프링부트 메이져 버젼을 업데이트 하며 가장 고생하였던 부분이 있어 집에서 간단하게 정리하고자 샘플 프로젝트를 만들었다. 스프링 부트 버젼 업데이트하며 세션에 저장되어 있는 값이 deserialize가 안되는 현상이 발생하였기 때문이다.

github_code 코드 참고

구성

  • 스프링 시큐리티
  • 레디스 세션
  • 기본 로그인
  • 스프링부트 버젼 2.1.5로 셋팅 -> 2장에서 2.2.4로 변경 예정

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
plugins {
id 'org.springframework.boot' version '2.1.5.RELEASE'
// id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}

group = 'com.beanbroker'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}



dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-core'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
compile('org.springframework.session:spring-session-data-redis')
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

}

dependencyManagement {

}

test {
useJUnitPlatform()
}

레디스 연동

아래의 디펜던시 추가

1
implementation 'org.springframework.session:spring-session-core'

docker에서 레디스 띄우기 및 cli 접속

1
2
3
docker run -p 6379:6379 --name pkj-redis -d redis

redis-cli -h 127.0.0.1 -p 6379

기본 application에 @EnableRedisHttpSession 추가

1
2
3
4
5
6
7
8
9
@EnableRedisHttpSession
@SpringBootApplication
public class BrokerApplication {

public static void main(String[] args) {
SpringApplication.run(BrokerApplication.class, args);
}

}

작동확인 방법

  • 대충 컨트롤러를 통해 데이터 넣고 cli를 통해 정상적으로 로우가 쌓였는지 확인
    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/test")
    public void test(HttpSession httpSession){

    httpSession.setAttribute("uid", UUID.randomUUID().toString());
    Object uid = httpSession.getAttribute("uid");


    }

Security설정

아래의 antMatchers등등은 직접 스프링 공홈에서 확인하는 습관을 가지자! 다 떠먹어선 안된다.

스프링 시큐리티 링크

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private final BrokerUserService brokerUserService;

public SecurityConfig(BrokerUserService brokerUserService) {
this.brokerUserService = brokerUserService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}


@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/test", "/", "/home", "/login", "/create").permitAll()
.antMatchers("/logout").hasRole(UserRole.USER.name())
.antMatchers("/admin").hasRole(UserRole.ADMIN.name())
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout();
}

}

유저를 만들어보자

아래와 같이 admin과 일반 유저 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@GetMapping("/set-up")
public BrokerUserEntity memberJoin() {

final BrokerUserEntity user = new BrokerUserEntity();
user.setId(UUID.randomUUID().toString());
user.setUserId("1234");
user.setPassword(passwordEncoder.encode("1234"));

brokerUserRepository.save(user);

final BrokerUserEntity admin = new BrokerUserEntity();
admin.setId(UUID.randomUUID().toString());
admin.setUserId("admin");
admin.setPassword(passwordEncoder.encode("1234"));

brokerUserRepository.save(user);

return user;
}

유저 권한 부여

UserDetailsService란? 이것 또한 직접 찾아보자!

id가 admin에 admin role부여
일반유저는 user권한 부여

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Service
public class BrokerUserService implements UserDetailsService {


private final BrokerUserRepository brokerUserRepository;


public BrokerUserService(BrokerUserRepository brokerUserRepository) {
this.brokerUserRepository = brokerUserRepository;

}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


BrokerUserEntity user = brokerUserRepository.findBrokerUserByUserId(username);

List<GrantedAuthority> grantedAuthorities = new ArrayList<>();


if (username.equals("admin")) {
grantedAuthorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.name()));
} else {
grantedAuthorities.add(new SimpleGrantedAuthority(UserRole.USER.name()));
}

return new User(user.getUserId(), user.getPassword(), grantedAuthorities);
}

}

코드 받고 테스트 방법

  1. localhost:8080/set-up : 아이디 2개 생성
  2. localhost:8080/login : 로그인하자
  • admin/1234
  • user/1234
  1. http://localhost:8080/users : UserDetails 정보
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"password": null,
"username": "1234",
"authorities": [
{
"authority": "USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
}

너무나도 불칠전한 설명이지만 이 프로젝트는 이게 중요한것이 아니다….