Skip to content

Commit

Permalink
Merge pull request #16 from Ajou-Travely/feature/kakao_login
Browse files Browse the repository at this point in the history
feature/kakao login
  • Loading branch information
park0503 authored Apr 28, 2022
2 parents 4b0754f + aabcb76 commit 2b6929a
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 16 deletions.
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ repositories {
dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// implementation 'org.springframework.boot:spring-boot-starter-security'
// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
// JSON Parser
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
// implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.session:spring-session-jdbc'

// swagger setting
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'mysql:mysql-connector-java'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2:1.4.200'
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/ajou/travely/config/CorsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ajou.travely.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:3000")
.allowCredentials(true);
}
};
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/ajou/travely/config/CustomAuthentication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.ajou.travely.config;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.io.Serializable;
import java.util.Collection;

public class CustomAuthentication implements Authentication, Serializable {
private final Long kakaoId;

public CustomAuthentication(Long kakaoId) {
this.kakaoId = kakaoId;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

@Override
public Object getCredentials() {
return null;
}

@Override
public Object getDetails() {
return kakaoId;
}

@Override
public Object getPrincipal() {
return null;
}

@Override
public boolean isAuthenticated() {
return true;
}

@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

}

@Override
public String getName() {
return null;
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/ajou/travely/config/RestTemplateConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ajou.travely.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/ajou/travely/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ajou.travely.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
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;

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/**").permitAll();
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/ajou/travely/controller/auth/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ajou.travely.controller.auth;

import com.ajou.travely.service.AuthService;

import lombok.RequiredArgsConstructor;
import org.json.simple.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


import java.util.Optional;

@RestController
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;

@GetMapping("/oauth2/authorization/kakao")
public JSONObject login(@RequestParam("code") String code) {
return authService.kakaoAuthentication(code);
}

@GetMapping("/isLogin")
public Boolean isLogin(@RequestHeader("Cookie") Optional<Object> header) {
if (header.isPresent()) {
return true;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import lombok.Getter;

@Getter
public class UserCreateRequestDto {
private String userType;
private String name;
private String email;
private String phoneNumber;
public class UserCreateRequestDto {
private String userType;
private String name;
private String email;
private String phoneNumber;
private Long kakaoId;

public User toEntity() {
return User.builder().type(Type.valueOf(this.userType)).name(this.name).email(this.email).phoneNumber(this.phoneNumber).build();
return User.builder().type(Type.valueOf(this.userType)).name(this.name).email(this.email).phoneNumber(this.phoneNumber).kakaoId(this.kakaoId).build();
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/ajou/travely/domain/AuthorizationKakao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ajou.travely.domain;

import lombok.Getter;

@Getter
public class AuthorizationKakao {
private String access_token;
private String token_type;
private String refresh_token;
private String expires_in;
private String scope;
private String refresh_token_expires_in;
}
5 changes: 4 additions & 1 deletion src/main/java/com/ajou/travely/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ public class User {
@Enumerated(EnumType.STRING)
private Mbti mbti;

private Long kakaoId;

private LocalDate birthday;

@Builder
public User(@NonNull Type type, @NonNull String email, @NonNull String name, @NonNull String phoneNumber) {
public User(@NonNull Type type, @NonNull String email, @NonNull String name, @NonNull String phoneNumber, @NonNull Long kakaoId) {
this.type = type;
this.email = email;
this.name = name;
this.phoneNumber = phoneNumber;
this.kakaoId = kakaoId;
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/ajou/travely/repository/UserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

import com.ajou.travely.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.kakaoId = :kakaoId")
public Optional<User> findByKakaoId(@Param("kakaoId") Long kakaoId);
}
20 changes: 20 additions & 0 deletions src/main/java/com/ajou/travely/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.ajou.travely.service;

import com.ajou.travely.domain.AuthorizationKakao;
import lombok.RequiredArgsConstructor;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthService {
private final Oauth2Service oauth2Service;

public JSONObject kakaoAuthentication(String code) {
AuthorizationKakao authorizationKakao = oauth2Service.callTokenApi(code);
JSONObject userInfoFromKakao = oauth2Service.callGetUserByAccessToken(authorizationKakao.getAccess_token());
Long kakaoId = (Long) userInfoFromKakao.get("id");
JSONObject result = oauth2Service.setSessionOrRedirectToSignUp(kakaoId);
return result;
}
}
116 changes: 116 additions & 0 deletions src/main/java/com/ajou/travely/service/Oauth2Service.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.ajou.travely.service;

import com.ajou.travely.config.CustomAuthentication;
import com.ajou.travely.domain.AuthorizationKakao;
import com.ajou.travely.domain.user.User;
import com.ajou.travely.repository.UserRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class Oauth2Service {
private final UserRepository userRepository;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
@Value("${auth.kakaoOauth2ClinetId}")
private String kakaoOauth2ClinetId;
@Value("${auth.frontendRedirectUrl}")
private String frontendRedirectUrl;

public AuthorizationKakao callTokenApi(String code) {
String grantType = "authorization_code";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", grantType);
params.add("client_id", kakaoOauth2ClinetId);
params.add("redirect_uri", frontendRedirectUrl + "oauth/kakao/callback");
params.add("code", code);

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
System.out.println("request.getBody() = " + request.getBody());
String url = "https://kauth.kakao.com/oauth/token";
try {
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
System.out.println("response.getBody() = " + response.getBody());
System.out.println("response.getHeaders() = " + response.getHeaders());
AuthorizationKakao authorization = objectMapper.readValue(response.getBody(), AuthorizationKakao.class);

return authorization;
} catch (RestClientException | JsonProcessingException ex) {
ex.printStackTrace();
throw new RestClientException("error");
}
}

public JSONObject callGetUserByAccessToken(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

String url = "https://kapi.kakao.com/v2/user/me";
try {
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
SecurityContext context = SecurityContextHolder.getContext();
JSONObject userInfo = stringToJson(response.getBody());
// System.out.println("userInfo.get(\"id\") = " + userInfo.get("id"));
// JSONObject properties = (JSONObject) userInfo.get("properties");
// System.out.println("properties.get(\"nickname\") = " + properties.get("nickname"));
// Long kakaoId = (Long) userInfo.get("id");
// context.setAuthentication(new CustomAuthentication(kakaoId));
return userInfo;
}catch (RestClientException | ParseException ex) {
ex.printStackTrace();
throw new RestClientException("error");
}
}

public JSONObject setSessionOrRedirectToSignUp(Long kakaoId) {
Optional<User> user = userRepository.findByKakaoId(kakaoId);
JSONObject result = new JSONObject();
if(!user.isPresent()) {
result.put("status", 301);
result.put("kakaoId", kakaoId);
return result;
} else {
SecurityContext context = SecurityContextHolder.getContext();
User exUser = user.get();
context.setAuthentication(new CustomAuthentication(kakaoId));
result.put("status", 200);
}
return result;
}
public JSONObject stringToJson(String userInfo) throws ParseException {
JSONParser jsonParser = new JSONParser();
Object object = jsonParser.parse(userInfo);
JSONObject jsonObject = (JSONObject) object;
return jsonObject;
}

}
17 changes: 12 additions & 5 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
spring:
datasource:
url: jdbc:mariadb://localhost:3306/travely
url: jdbc:mysql://localhost:3306/travely
username: root
password: password
driver-class-name: org.mariadb.jdbc.Driver
password: ajoulee1214
driver-class-name: com.mysql.jdbc.Driver
# swagger setting
mvc:
pathmatch:
matching-strategy: ant_path_matcher

session:
store-type: jdbc
jdbc:
initialize-schema: always

jpa:
# hibernate:
# ddl-auto: create
hibernate:
ddl-auto: create
properties:
hibernate:
show_sql: true
format_sql: true
profiles:
include:
- "auth"
active: default
jackson:
serialization.WRITE_DATES_AS_TIMESTAMPS=false
Expand Down
Loading

0 comments on commit 2b6929a

Please sign in to comment.