Skip to content

Commit

Permalink
OAuth 2.0 Authorization Server, JWT 토큰 발급 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
pompitzz committed Dec 23, 2019
1 parent 8d757b1 commit 2f07c8d
Show file tree
Hide file tree
Showing 21 changed files with 409 additions and 48 deletions.
3 changes: 3 additions & 0 deletions spring-boot-study/build.gradle
Expand Up @@ -21,11 +21,14 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.2.2.RELEASE'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'

annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.security:spring-security-test'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
Expand Down
@@ -0,0 +1,27 @@
package me.sun.springbootstudy;

import lombok.RequiredArgsConstructor;
import me.sun.springbootstudy.domain.member.MemberRole;
import me.sun.springbootstudy.domain.member.MemberService;
import me.sun.springbootstudy.web.dto.MemberJoinRequestDto;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class AppRunner implements ApplicationRunner {

private final MemberService memberService;

@Override
public void run(ApplicationArguments args) throws Exception {
MemberJoinRequestDto dto = MemberJoinRequestDto.builder()
.email("test@gmail.com")
.password("qwe123")
.role(MemberRole.USER)
.name("홍길동")
.build();
memberService.save(dto);
}
}
@@ -0,0 +1,15 @@
package me.sun.springbootstudy.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class AppConfig {

@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
@@ -0,0 +1,69 @@
package me.sun.springbootstudy.config;

import lombok.RequiredArgsConstructor;
import me.sun.springbootstudy.domain.member.MemberService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@RequiredArgsConstructor
@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final MemberService memberService;

@Value("${custom.clientId}")
private String clientId;
@Value("${custom.clientSecret}")
private String clientSecret;
@Value("${custom.jwtKey}")
private String jwtKey;

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder);
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "profile")
.secret(passwordEncoder.encode(clientSecret))
.accessTokenValiditySeconds(10 * 60)
.refreshTokenValiditySeconds(6 * 10 * 60);
}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(memberService)
.tokenStore(tokenStore())
.accessTokenConverter(jwtAccessTokenConverter());
}

@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}

private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(jwtKey);
return converter;
}
}
@@ -0,0 +1,44 @@
package me.sun.springbootstudy.config;

import lombok.RequiredArgsConstructor;
import me.sun.springbootstudy.domain.member.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.password.PasswordEncoder;

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private final MemberService memberService;
private final PasswordEncoder passwordEncoder;

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService)
.passwordEncoder(passwordEncoder);
}


@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/members/join").permitAll()
.anyRequest().authenticated();

}
}
@@ -1,4 +1,4 @@
package me.sun.springbootstudy.member;
package me.sun.springbootstudy.domain.member;

import lombok.AccessLevel;
import lombok.Builder;
Expand Down Expand Up @@ -35,4 +35,8 @@ public Member(String email, String password, String name, MemberRole role) {
this.name = name;
this.role = role;
}

public void encodingPassword(String password) {
this.password = password;
}
}
@@ -1,4 +1,4 @@
package me.sun.springbootstudy.member;
package me.sun.springbootstudy.domain.member;


import org.springframework.data.jpa.repository.JpaRepository;
Expand Down
@@ -0,0 +1,5 @@
package me.sun.springbootstudy.domain.member;

public enum MemberRole {
ADMIN, USER
}
@@ -0,0 +1,52 @@
package me.sun.springbootstudy.domain.member;

import lombok.RequiredArgsConstructor;
import me.sun.springbootstudy.web.dto.MemberJoinRequestDto;
import me.sun.springbootstudy.web.dto.MemberResponseDto;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;

@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class MemberService implements UserDetailsService {

private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;

@Transactional
public Long save(MemberJoinRequestDto dto) {
Member member = dto.toEntity();
member.encodingPassword(passwordEncoder.encode(member.getPassword()));
return memberRepository.save(member).getId();
}

public MemberResponseDto findOne(Long id) {
Member member = memberRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("해당 유저가 존재하지 않습니다."));

return new MemberResponseDto(member);
}

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email));

return new User(member.getEmail(), member.getPassword(), authorities(member.getRole()));
}

private Collection<? extends GrantedAuthority> authorities(MemberRole role) {
return Collections.singleton(new SimpleGrantedAuthority("ROLE_" + role.toString()));
}
}

This file was deleted.

This file was deleted.

@@ -0,0 +1,33 @@
package me.sun.springbootstudy.web;

import lombok.RequiredArgsConstructor;
import me.sun.springbootstudy.domain.member.MemberService;
import me.sun.springbootstudy.web.dto.MemberJoinRequestDto;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/members")
public class MemberApiController {

private final MemberService memberService;

@PostMapping("/join")
public ResponseEntity joinMember(@RequestBody @Valid MemberJoinRequestDto dto,
Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
memberService.save(dto);

return ResponseEntity.ok().build();
}

}

This file was deleted.

Expand Up @@ -3,16 +3,23 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import me.sun.springbootstudy.member.Member;
import me.sun.springbootstudy.member.MemberRole;
import me.sun.springbootstudy.domain.member.Member;
import me.sun.springbootstudy.domain.member.MemberRole;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@NoArgsConstructor
@Getter
public class MemberJoinRequestDto {

@NotEmpty
private String email;
@NotEmpty
private String password;
@NotEmpty
private String name;
@NotNull
private MemberRole role;

@Builder
Expand Down
Expand Up @@ -2,8 +2,8 @@

import lombok.Getter;
import lombok.NoArgsConstructor;
import me.sun.springbootstudy.member.Member;
import me.sun.springbootstudy.member.MemberRole;
import me.sun.springbootstudy.domain.member.Member;
import me.sun.springbootstudy.domain.member.MemberRole;

@Getter
@NoArgsConstructor
Expand Down
7 changes: 6 additions & 1 deletion spring-boot-study/src/main/resources/application.yml
Expand Up @@ -10,4 +10,9 @@ spring:
properties:
hibernate:
format_sql: true
show_sql: true
show_sql: true

custom:
clientId: clientApp
clientSecret: secret
jwtKey: test

0 comments on commit 2f07c8d

Please sign in to comment.