https://developers.naver.com/apps
애플리케이션 - NAVER Developers
developers.naver.com
해당 사이트에 들어가 원하는 api를 선택한다. 지금은 테스트이기 떄문에 회원이름과 이메일의 정보를 받아오도록 하겠다.
주소를 입력하고 받아올 정보를 넘길곳을 지정해준다.
https://console.developers.google.com/
새 프로젝트 만들기를 선택 후 이름을 넣어준다.
위에 있는 링크를 다시한번 들어가게 되면 왼쪽에 아래의 사진과 같은 메뉴를 볼 수 있다.
인증정보 메뉴를 선택 후 사용자 인증정보 만들기를 누른다. 그 후 OAuth 클라이언트 ID 만들기를 누르고 동의화면만들기를 누른다. 외부 선택 후 만들기
다시 사용자 인증정보 만들기에 들어가 OAuth 클라이언트 ID 만들기를 누르고 웹 어플리케이션을 선택한다.
URL | https://localhost:8080 |
승인된 리다이렉션 URL | http://localhost:8080/login/oauth2/code/google |
생성된 아이디와 비밀번호를 따로 적어두어야 한다.
프로젝트 생성
Loombok에 에러가 날 가능성이 높기 때문에 이번에도 start.spring.io를 통해 프로젝트를 만들었다.
hello.html생성(인증성공 후 페이지)
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h1>인증 성공</h1>
<ul>
<li>User name : <span th:text="${#authentication.principal.attributes.email}"></span></li>
<li>Authorities : <span th:text="${#authentication.principal.authorities}"></span></li>
</ul>
<form th:action="@{/logout}" method="get">
<button th:if="${#authentication ne null}">Logout</button>
</form>
</body>
</html>
API를 통한 보안인증은 가져온 Email, 이름을 attribute에 집어넣을 예정이다.
home.htm
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h1>Home Page</h1>
<a th:href="@{/login}">로그인</a>
</body>
</html>
login.html생성
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
<script th:src="@{/js/jquery-3.6.0.min.js}"></script>
<script>
$(function(){
$('a').bind("click", function(){
location.href = "/oauth2/authorization/" + $(this).attr("data-social");
});
});
</script>
</head>
<body>
<h1>Login Page</h1>
<a href="#" class="btn_social" data-social="google">Google</a><br />
<a href="#" class="btn_social" data-social="naver">Naver</a><br />
</body>
</html>
Jquery를 이용하여 구글을 클릭시 구글로 네이버로 클릭시 네이버로 로그인가능하게 하는 창으로 넘어가게한다.
loginFailure.html생성(로그인 실패)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h1>인증 실패</h1>
<a th:href="@{/login}">Login Again</a>
</body>
</html>
logout.html생성
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
</head>
<body>
<h2>Custom Logout Page</h2>
<form method="POST">
<h3>Logout</h3>
<input type="hidden" th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
<button type="submit" class="btn btn-primary">Logout</button>
</form>
</body>
</html>
application.properties
spring.thymeleaf.cache=false
spring.security.oauth2.client.registration.google.client-id=클라이언트 아이디
spring.security.oauth2.client.registration.google.client-secret= 비밀번호
spring.security.oauth2.client.registration.google.scope=profile,email
spring.security.oauth2.client.registration.naver.client-id=클라이언트 아이디
spring.security.oauth2.client.registration.naver.client-secret=비밀번호
spring.security.oauth2.client.registration.naver.redirect-uri={baseUrl}/{action}/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.scope=name,email
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response
모든설정은 application.properties에서 한다.
기본적으로 클라이언트 아이디와 비밀번호가 필요한데 oauth인증시에는 추가적인 정보가 더 들어간다.
Naver제일 하단에 보면 response가 붙는것을 확인할 수 있는데 갔다올 때 json형태로 오는 키값이다.
OAuth2Controller.java생성
@Slf4j
@Controller
public class OAuth2Controller {
@GetMapping({ "", "/" })
public String getAuthorizationMessage() {
return "home";
}
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping({ "/loginSuccess", "/hello" })
public String loginSuccess(Principal principal, Model model) {
return "hello";
}
@GetMapping("/loginFailure")
public String loginFailure() {
return "loginFailure";
}
@GetMapping("/logout")
public void logout() {
log.info("called Logout page");
}
}
OAuthAttributes.java생성
@Slf4j
@Getter
public class OAuthAttributes {
private Map<String, Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
@Builder
public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email) {
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.email = email;
}
public static OAuthAttributes of(String registrationId, String userNameAttributeName,
Map<String, Object> attributes) {
if ("naver".equals(registrationId)) {
return ofNaver("id", attributes);
}
return ofGoogle(userNameAttributeName, attributes);
}
private static OAuthAttributes ofGoogle(String userNameAttributeName, Map<String, Object> attributes) {
return OAuthAttributes.builder().name((String) attributes.get("name")).email((String) attributes.get("email"))
.attributes(attributes).nameAttributeKey(userNameAttributeName).build();
}
private static OAuthAttributes ofNaver(String userNameAttributeName, Map<String, Object> attributes) {
Map<String, Object> response = (Map<String, Object>) attributes.get("response");
return OAuthAttributes.builder().email((String) response.get("email")).name((String) response.get("name"))
.attributes(response).nameAttributeKey(userNameAttributeName).build();
}
}
위에서 언급했다시피 Attribute안에 정보(Email, 이름)를 집어넣는다.
builder라는 Annotaion을 통해 Attribute를 Setting을 한다.
네이버를 요청했다면 네이버method로 가고 Google을 요청했다면 Google로 이동하게한다.
Application.properties에서 언급했던 Response라는 키값을 이용하여 정보를 가져온다.
CustomOAuth2UserService.java
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService delegate = new DefaultOAuth2UserService(); // UserService를 하나 만든다.
OAuth2User oAuth2User = delegate.loadUser(userRequest); // 요청 request에 의해 유저를 만들면 이름과 이메일이 들어온다.
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint() //Endpoint() : 정보가 저장되어있는 곳이다.
.getUserNameAttributeName();
OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes()); // 네이버 또는 구글의 인증을 가져온다
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
authorities.add(new OAuth2UserAuthority(attributes.getAttributes())); //정보 담기
OAuth2AccessToken token = userRequest.getAccessToken(); // AccessToken() = principle
for (String authority : token.getScopes()) {
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority)); // Role_도 있지만 SCOPE_도 있다.
}
return new DefaultOAuth2User(authorities, attributes.getAttributes(), attributes.getNameAttributeKey());
}
}
<OAuth2UserRequest> : 받아오는 값이 naver, Google요청하느냐에 따라 값이 변하게 map으로 처리한다
OAuth2UserService interface를 상속받으며 OAuth2User 값을 리턴한다.
SocialType enum 생성
public enum SocialType {
FACEBOOK("facebook"), GOOGLE("google"), KAKAO("kakao"), NAVER("naver"); //반드시 enum은 상수를 먼저 써야한다.
private final String ROLE_PREFIX = "ROLE_";
private String name;
SocialType(String name) { //앞에 public을 사용하면안된다.
this.name = name;
}
public String getRoleType() {
return ROLE_PREFIX + name.toUpperCase();
}
public String getValue() {
return name;
}
public boolean isEquals(String authority) {
return this.getRoleType().equals(authority);
}
}
SecurityConfig.java 생성
package com.example.oauth2.security;
import static com.example.oauth2.security.SocialType.FACEBOOK;
import static com.example.oauth2.security.SocialType.GOOGLE;
import static com.example.oauth2.security.SocialType.KAKAO;
import static com.example.oauth2.security.SocialType.NAVER;
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.web.authentication.LoginUrlAuthenticationEntryPoint;
import com.example.service.CustomOAuth2UserService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws java.lang.Exception{
http.authorizeRequests().antMatchers("/", "/oauth2/**", "/login/**", "/css/**","/images/**", "/js/**", "/console/**","/favicon.ico/**")
.permitAll()
.antMatchers("/facebook").hasAuthority(FACEBOOK.getRoleType())
.antMatchers("/google").hasAuthority(GOOGLE.getRoleType())
.antMatchers("/kakao").hasAuthority(KAKAO.getRoleType())
.antMatchers("/naver").hasAuthority(NAVER.getRoleType())
.anyRequest().authenticated()
.and()
.oauth2Login() //oauth2인증 받아오기
.userInfoEndpoint().userService(new CustomOAuth2UserService())
.and()
.defaultSuccessUrl("/loginSuccess")
.failureUrl("/loginFailure")
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")); //
http.logout().logoutUrl("/logout").invalidateHttpSession(true).logoutSuccessUrl("/login"); //로그아웃하면 다시 로그인 창으로 가게 설정한다.
}
}
WebSecurityConfigurerAdapter을 상속받는다.
스태틱 import가 잘 안들어오기 때문에 import문을 따로 작성해준다(KAKAO, GOOGLE, NAVER, FACEBOOK).
'SpringBoot 코딩' 카테고리의 다른 글
11. Thymeleaf의 sec nameSpace가 없을 때 사용하는 방법 (SpringBootSecurity Demo) (0) | 2021.12.12 |
---|---|
10. DB와 연동한 Security, Password Encording (SpringSecurity) (0) | 2021.12.11 |
9. Spring SeCurity (SpringBootSecurityDemo) (0) | 2021.12.11 |
8. RestFulApi를 이용한 프로젝트만들기, war packaging 진짜 Tomcat사용하기 (demo) (0) | 2021.12.09 |
7.2 Mybatis와 hikariCP를 이용한 프로젝트 만들기(BootJdbcDemo) (0) | 2021.11.26 |