GitHub - jisu3316/Spring-Security: OAuth2
OAuth2 . Contribute to jisu3316/Spring-Security development by creating an account on GitHub....
github.com
OAuth란?
OAuth(Open Authoriztion)는 토큰 기반의 인증 및 권한을 위한 표준 프로토콜입니다. OAuth와 같은 인증 프로토콜을 통해 유저의 정보를 페이스북, 구글, 네이버 등의 서비스에서 제공받을 수 있고 이정보를 기반으로 어플리케이션 사용자에게 로그인이나 다른 여러 기능을 손쉽게 제공 할 수 있습니다. 자세한 내용은 생활코딩님의 유튜브영상을 참조 하시면 좋을것같습니다.
스프링부트로 OAu2를 통한 로그인 기능
스프링 부트로 OAuth2를 통한 로그인 기능을 제공할 수 있습니다. 여기서는 페이스북, 구글 ,네이버에서 제공하는 정보를 통해 사용자가 웹페이지에 손쉽게 로그인하는 기능을 구현할 것입니다. 스프링부트로 페이스북,구글 ,네이버 개발 페이지에 가서 인증설정을 해야합니다.
프로젝트 구조
\---src
+---main
| +---java
| | \---com
| | \---example
| | \---security
| | | SecurityApplication.java
| | |
| | +---config
| | | |--SecurityConfig.java
| | | |--WebMvcConfig.java
| | | +---auth
| | | | PricipalDetails.java
| | | | PricipalDetailsService.java
| | | +---oauth
| | | | PrincipalOauth2UserService.java
| | | +---provider
| | | FaceBookUserInfo.java
| | | GoogleUserInfo.java
| | | NaverUserInfo.java
| | +---controller
| | | IndexController.java
| | |
| | |---model
| | | User.java
| | |
| | \---repository
| | UserRepository.java
| |
| \---resources
| | application.yml
| |
| +---static
| \---templates
| Index.html
| joinForm.html
| loginForm.html
의존성
plugins {
id 'org.springframework.boot' version '2.6.8'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.6.0'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
server:
port: 8080
servlet:
context-path: /
encoding:
charset: UTF-8
enabled: true
force: true
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/security?useUnicode=true&characterEncoding=utf-8
username: cos
password: cos1234
jpa:
hibernate:
ddl-auto: update #create update none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
properties:
hibernate:
format_sql: true
security:
oauth2:
client:
registration:
naver:
client-id: 발급받은 키 값
client-secret: 발급받은 키 값
redirect-uri: "{baseUrl}/{action}/oauth2/code/{registrationId}" # (== http://localhost:8080/login/oauth2/code/naver)
authorization-grant-type: authorization_code
scope: name, email, profile_image
client-name: Naver
google:
client-id: 발급받은 키 값
client-secret: 발급받은 키 값
scope:
- email
- profile
facebook:
client-id: 발급받은 키 값
client-secret: 발급받은 키 값
scope:
- email
- public_profile
provider:
naver:
authorization_uri: https://nid.naver.com/oauth2.0/authorize
token_uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user_name_attribute: response
logging:
level:
org.hibernate.SQL: debug
org.hibernate.type: trace
소스코드
SecurityApplication ( main 진입점 )
package com.example.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
IndexController
package com.example.security.controller;
import com.example.security.config.auth.PrincipalDetails;
import com.example.security.model.User;
import com.example.security.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
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
@RequiredArgsConstructor
public class IndexController {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/test/login")
public @ResponseBody String loginTest(Authentication authentication, @AuthenticationPrincipal PrincipalDetails userDetails) { //DI(의존성 주입)
System.out.println("/test/login ===================");
PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println("authentication = " + principalDetails.getUser());
System.out.println("userDetails = " + userDetails.getUser());
return "세션 정보 확인하기";
}
@GetMapping("/test/oauth/login")
public @ResponseBody String testOAuthlogin(Authentication authentication, @AuthenticationPrincipal OAuth2User oauth) { //DI(의존성 주입)
System.out.println("/test/oauth/login ===================");
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
System.out.println("oAuth2User.getAttributes() = " + oAuth2User.getAttributes());
System.out.println("oauth.getAttributes(): "+oauth.getAttributes());
return "OAuth 세션 정보 확인하기";
}
@ResponseBody
@GetMapping({"", "/"})
public String index() {
//머스테치 기본폴더 src/main/resources/
//뷰리졸버 설정 : templates(prefix), .mustache( suffix) 생략가능!!
//src/main/resources/templates/index.mustache
return "hello";
}
//OAuth 로그인을 해도 PrincipalDetails 받을 수 있음
//일반 로그인을 해도 PrincipalDetails 받을 수 있음
@ResponseBody
@GetMapping("/user")
public String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
System.out.println("principalDetails = " + principalDetails.getUser());
return "user";
}
@ResponseBody
@GetMapping("/admin")
public String admin() {
return "admin";
}
@ResponseBody
@GetMapping("/manager")
public String manager() {
return "manager";
}
//스프링 시큐리티 해당주소를 낚아채간다. -SecurityConfig 파일 생성 후 작동안함.
@GetMapping("/loginForm")
public String loginForm() {
return "loginForm";
}
@GetMapping("/joinForm")
public String joinForm() {
return "joinForm";
}
@PostMapping("/join")
public String join(User user) {
System.out.println(user);
user.setRole("ROLE_USER");
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user); //회원가입 잘됨. 이렇게하면 비밀번호가 1234로 들어감 => 시큐리티로 로그인을 할 수없음. 패스워드가 암호화가 안되었기 때문에.
return "redirect:/loginForm";
}
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
@PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")//함수가 시작전에.
@GetMapping("/data")
public @ResponseBody String data() {
return "데이터정보";
}
}
SecurityConfig
package com.example.security.config;
import com.example.security.config.oauth.PrincipalOauth2UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
// 구글 로그인이 완료된 뒤의 후처리가 필요함.
// 1. 코드받기(인증을받음,구글에 로그인이 됨), 2.엑세스토큰을받음(사용자정보에 접근 권한가능), 3.사용자 프로필 정보를 가져오고, 4-1. 그 정보를 토대로 회원가입을 자동으로 진행시키도록 함
// 4-2(이메일,전화번호,이름,아이디) 쇼핑몰 -> (집주소),백화점몰 ->(vip등급,일반등급)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //secured 어노테이션 활성화, preAuthorize,postAuthorize 어노테이션 활성화
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final PrincipalOauth2UserService principalOauth2UserService;
public SecurityConfig(PrincipalOauth2UserService principalOauth2UserService) {
this.principalOauth2UserService = principalOauth2UserService;
}
//해당 메서드의 리턴되는 오브젝트를 IoC로 등록해준다.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // /user/** 밑으로 들어오는 url는 인증이 필요하다.
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')") //hasRole 권한이 있어야한다.
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll() //나머지 주소는 권한이 허용이된다.
.and()
.formLogin()
.loginPage("/loginForm")
//.usernameParameter("username2") form에서 name값을 바꿔주려면 parameter 바꿔줘야함
.loginProcessingUrl("/login") //login 조수가 호출이되면 시큐리가 낚아채서 대신 로그인을 진행해줍니다.
.defaultSuccessUrl("/")
.and()
.oauth2Login()
.loginPage("/loginForm") // 구글 로그인이 완료된 뒤의 후처리가 필요함. Tip.코드X, (엑세스토큰+ 사용자 프로필정보O)
.userInfoEndpoint()
.userService(principalOauth2UserService);
}
}
- http.csrf().disable : csrf보호기능을 disable > csrf에 대한 것은 글을 한번 다뤄보겠습니다..
- authorizeRequests() : 시큐리티 처리에 HttpServlectRequest를 이용하겠다.
- antMatchers() : 특정한 경로를 지정합니다.
- access(String) : 주어진 SpEl 표현식의 평가 결과가 true이면 접근을 허용하겠다.
- hasRole(String) : 사용자가 주어진 역할이 있다면 접근을 허용하겠다.
- anyRequest() : 그 외 경로들을 뜻합니다.
- permitAll() : 무조건 접근을 허용
- loginPage("/loginForm") : 인증이 필요할 때 이동하는 페이지 설정하는 api입니다. 해당 api설정을 하지 않을 경우 spring security가 제공하는 템플릿으로 연결됩니다.
- defaultSuccessUrl("/") : 인증이 성공하였을때 default로 이동하는 URL을 지정하는 api 입니다.
- usernameParameter("username") : spring security 에서 기본값인 username,password 파라미터의 값을 변경하게 해주는 api입니다.
- loginProcessingUrl("/login") : 폼테그의 action url을 설정하는 api입니다. default 값은 default login 입니다.
여기까지 하면 기본적인 로그인을 할 때의 기능들입니다. 그리고 저는 oauth2Login을 사용할것이기 때문에 추가를 해줍니다.
securty를 적용하려면 spring boot 2.7.0 버전 미만은 WebSecurtiyConfigurerAdapter를 상속받아 configure 메소드를 구현 해줘야 합니다.
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //secured 어노테이션 활성화, preAuthorize,postAuthorize 어노테이션 활성화
public class SecurityConfig {
private final PrincipalOauth2UserService principalOauth2UserService;
public SecurityConfig(PrincipalOauth2UserService principalOauth2UserService) {
this.principalOauth2UserService = principalOauth2UserService;
}
//해당 메서드의 리턴되는 오브젝트를 IoC로 등록해준다.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // /user/** 밑으로 들어오는 url는 인증이 필요하다.
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')") //hasRole 권한이 있어야한다.
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll() //나머지 주소는 권한이 허용이된다.
.and()
.formLogin()
.loginPage("/loginForm")
//.usernameParameter("username2") form에서 name값을 바꿔주려면 parameter 바꿔줘야함
.loginProcessingUrl("/login") //login 조수가 호출이되면 시큐리가 낚아채서 대신 로그인을 진행해줍니다.
.defaultSuccessUrl("/")
.and()
.oauth2Login()
.loginPage("/loginForm") // 구글 로그인이 완료된 뒤의 후처리가 필요함. Tip.코드X, (엑세스토큰+ 사용자 프로필정보O)
.userInfoEndpoint()
.userService(principalOauth2UserService);
return http.build();
}
spring boot 2.7.0 버전 이상부터는 위와 같이 configure 대신 SecurityFilterChain을 빈으로 등록해줘서 사용해줘야합니다.
ref: https://www.codejava.net/frameworks/spring-boot/fix-websecurityconfigureradapter-deprecated
PrincipalDetails.java
package com.example.security.config.auth;
// 시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
// 로그인을 진행이 완료가 되면 시큐리티 session을 만들어 줍니다. (Security ContextHolder)
// 시큐리티에 들어갈수있는 정보가 정해져있다. 오브젝트 => Authentication 타입 객체
// Authentication 안에 User 정보가 있어야 됨.
// User오브젝트 타입 => UserDetails 타입 객체
//Security Session 영역이 있음. => Authentication => UserDetails(PrincipalDetails) 타입이여야함.
//Security Session을 꺼내면 Authentication 객체가 나옴. UserDetails을 꺼내면 User정보에 접근가능함.
import com.example.security.model.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@Data
public class PrincipalDetails implements UserDetails, OAuth2User {
private User user;
private Map<String,Object> attributes;
//일반 로그인
public PrincipalDetails(User user) {
this.user = user;
}
//OAuth 로그인
public PrincipalDetails(User user, Map<String, Object> attributes) {
this.user = user;
this.attributes = attributes;
}
//해당 유저의 권한을 리턴하는 곳!!
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return collect;
}
//패스워드 리턴
@Override
public String getPassword() {
return user.getPassword();
}
//유저이름 리턴턴
@Override
public String getUsername() {
return user.getUsername();
}
//계정 만료되었는지 true ==만료되지 않았다.
@Override
public boolean isAccountNonExpired() {
return true;
}
//계정 잠겼니 true ==안잠겼다
@Override
public boolean isAccountNonLocked() {
return true;
}
//계정의 비밀번호 1년이 지났니 true = 아니요
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//계정이 활성화 되었니 true = 아니요
@Override
public boolean isEnabled() {
//우리 사이트에서 1년동안 회원이 로그인을 안하면!! 휴면계정으로 하기로 함.
//현재시간 - 로그인 시간 => 1년을 초가화면 return false; 하면됨.
return true;
}
//OAuth2User 구현 메소드
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public String getName() {
return (String) attributes.get("sub");
}
}
PrincipalDeltailsService.java
package com.example.security.config.auth;
import com.example.security.model.User;
import com.example.security.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;
// 시큐리티 설정에서 loginProcessingUrl("/login");
// /login 요청이 오면 자동으로 UserDetailsService 타입으로 Ioc되어 있는 loadUserByUsername 함수가 실행
@Service
@RequiredArgsConstructor
public class PrincipalDetailsService implements UserDetailsService {
private final UserRepository userRepository;
// 시큐리티 session(내부 Authentication(내부 UserDetails)) = Authentication(내부 UserDetails) = UserDetails
//함수 종료시 @AuthenticationPrincipal 어노테이션 만들어진다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username: "+username);
User userEntity = userRepository.findByUsername(username);
if (userEntity != null) {
return new PrincipalDetails(userEntity);
}
return null;
}
}
그리고 oauth2 구글,네이버,페이스북 등으로 로그인하기를 눌렀을때 바로 회원가입이 되서 서비스를 이용하는데 지장이 없도록 구현하였습니다.
PrincipalOauth2UseService.java
package com.example.security.config.oauth;
import com.example.security.config.auth.PrincipalDetails;
import com.example.security.config.oauth.provider.FacebookUserInfo;
import com.example.security.config.oauth.provider.GoogleUserInfo;
import com.example.security.config.oauth.provider.NaverUserInfo;
import com.example.security.config.oauth.provider.OAuth2UserInfo;
import com.example.security.model.User;
import com.example.security.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final UserRepository userRepository;
public PrincipalOauth2UserService(@Lazy BCryptPasswordEncoder bCryptPasswordEncoder, @Lazy UserRepository userRepository) {
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.userRepository = userRepository;
}
//구글로 부터 받은 userRequest 데이터에 대한 후처리되는 함수
//함수 종료시 @AuthenticationPrincipal 어노테이션 만들어진다.
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
// 구글로그인 버튼 클릭 -> 구글 로그인창 -> 로그인을 완료 -> code를 리넡(OAuth-Client라이브러리) -> AccecssToken요청
// userRequest정보 -> loadUser함수 호출() -> 구글로 부터 회원 프로필을 받을 수 있음
System.out.println("super.loadUser(userRequest).getAttribute(): "+oAuth2User.getAttributes());
//회원가입을 강제로 진행
OAuth2UserInfo oAuth2UserInfo = null;
if (userRequest.getClientRegistration().getRegistrationId().equals("google")) {
System.out.println("구글로그인 요청");
oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
} else if (userRequest.getClientRegistration().getRegistrationId().equals("facebook")) {
System.out.println("페이스북 로그인 요청");
oAuth2UserInfo = new FacebookUserInfo(oAuth2User.getAttributes());
} else if (userRequest.getClientRegistration().getRegistrationId().equals("naver")) {
System.out.println("네이버 로그인 요청");
oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"));
}else {
System.out.println("우리는 구글과 페이스북,네이버만 지원해요.");
}
String provider = oAuth2UserInfo.getProvider();
String providerId = oAuth2UserInfo.getProviderId();
String username = provider+"_"+providerId; //username = google_107438726388440656699
String password = bCryptPasswordEncoder.encode("지수");
String email = oAuth2UserInfo.getEmail();
String role = "ROLE_USER";
User userEntity = userRepository.findByUsername(username);
if (userEntity == null) {
System.out.println("OAuth 로그인이 최초입니다.");
userEntity = User.builder()
.username(username)
.password(password)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
} else {
System.out.println("로그인을 이미 한적이 있습니다. 당신은 자동회원가입이 되어 있습니다.");
}
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
}
OAuth2UserInfo.java
package com.example.security.config.oauth.provider;
public interface OAuth2UserInfo {
String getProviderId();
String getProvider();
String getEmail();
String getName();
}
인터페이스를 만들어 상속받게 만들었습니다.
FaceboockUserInfo
package com.example.security.config.oauth.provider;
import java.util.Map;
public class FacebookUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes; //oauth2User.getAttributes()
public FacebookUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String) attributes.get("id");
}
@Override
public String getProvider() {
return "facebook";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
GoogleUserInfo
package com.example.security.config.oauth.provider;
import java.util.Map;
public class GoogleUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes; //oauth2User.getAttributes()
public GoogleUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String) attributes.get("sub");
}
@Override
public String getProvider() {
return "google";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
NaverUserInfo
package com.example.security.config.oauth.provider;
import java.util.Map;
public class NaverUserInfo implements OAuth2UserInfo {
private Map<String, Object> attributes; //oauth2User.getAttributes()
public NaverUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String) attributes.get("id");
}
@Override
public String getProvider() {
return "naver";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
'Spring Boot' 카테고리의 다른 글
[Git] Permission denied (publickey). fatal- Could not read from remote repository (0) | 2022.10.09 |
---|---|
Spring WebSocket (1) | 2022.09.09 |
[Spring] DTO는 왜 써야 하나요? (0) | 2022.08.18 |
[Spring Boot] Bean Validation (0) | 2022.06.28 |
[Spring Boot] 메세지, 국제화 관리 기능 (0) | 2022.06.28 |
댓글