월 24,200원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결스프링 시큐리티 OAuth2
강의에 사용하는 keycloak 커넥션 에러 질문드립니다
안녕하세요.강의 수강중 잘 이용해왔던 키클록이갑자기 에러가나서 테스트를 못하고있습니다재설치도 해보았는데 똑같더라구요. ㅠㅠ해당 에러 질문드립니다..
- 해결됨스프링 시큐리티 OAuth2
Authorization server 에서 localhost 를 사용하지 못하는 이유가 있을까요?
안녕하세요.말씀해주신 대로 호스트를 localhost 로 사용하는 경우에는 17::24 화면처럼 세션을 찾을수 없는 문제가 있는것을 확인하였습니다.redirect uri 를 모두 localhost 로 세팅 하였을때도 문제가 있는것을 확인했는데요, 그렇다면 authorization server 에서 로그인 성공시 host 가 localhost 이면 별도로 쿠키에 세팅을 해주지 않는다는 의미인가 해서 보니 권한 동의 이후 /oauth2/authorize 로 submit 을 할 Authorization Server 의 OAuth2AuthorizationEndpointFilter.java 에서 187 line 의 sessionAuthenticationStrategy 에서는 정상적으로 세션을 세팅하는것 같아보였습니다. (아래는 세팅되는 OAuth2AuthorizationServerConfigurer.java 282line. 의 lamda 로직입니다그렇다면 Client 쪽에서 같은 localhost 의 쿠키 id를 가져와서 사용하지 못한다는 말 같은데, 제가 빠뜨린 부분이 있는지 확인부탁드리겠습니다.
- 해결됨스프링 시큐리티 OAuth2
code verifier 질문 있습니다.
안녕하세요 강사님.시큐리티에 이어 OAuth 강의까지 잘 듣고 있습니다.PKCE 학습 중 Code Verifier에 관해 찾아보다 강의 내용과 조금 다른 부분이 있어 질문 드립니다.Code Verifier에서 48~128 글자수를 가진 무작위 문자열이라고 설명이 되어 있는데, RFC 문서를 보니까 43 ~128 글자수라고 설명이 되어 있습니다.https://www.rfc-editor.org/rfc/rfc7636혹시 어떤 내용이 맞는건지 확인 부탁드리겠습니다
- 해결됨스프링 시큐리티 OAuth2
Spring Authorization Server 을 OAuth Client Client 로 등록
Spring Authorization Server 1.x 와 OAuthClient 를 사용하고 있습니다.강의 내용과 레퍼런스를 참조하여 Spring Authorization Server 사용하여 SSO 시스템을 개발을 진행하고 있습니다.OAuth2 Client 등록등 관리자 화면 접속을 위하여 로그인 페이가 필요한데 개발중인 SSO 시스템을 OAuth2 Client 로 등록하여 인증처리를 하기 위하여 설정 파일에 provider 로 등록하였습니다.spring: security: oauth2: client: registration: local: authorization-grant-type: authorization_code client-id: 'default' client-secret: '325ee4c4-94e9-435b-a3c4-579f84d1e211' client-name: '인증서버' redirect-uri: SSO서버URL/social-login/oauth2/code/naon-iam scope: - openid - profile provider: local: issuer-uri: SSO서버URL문제는 Spring Boot 가 기동될떄 oauth2 client 설정이 초기화 되는 과자에서 issuer-uri 에 접속을 하는데 아직 WAS 가 실행이 안된 상태이기 때문에 issuer-uri 로 ODIC Provider 정보를 가져 올떄 Connection refused 예외가 발생합니다.Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://SSO서버URL/.well-known/openid-configuration": Connection refused: no further information at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:888) ~[spring-web-6.0.9.jar:6.0.9] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:868) ~[spring-web-6.0.9.jar:6.0.9] at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:714) ~[spring-web-6.0.9.jar:6.0.9] at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:163) ~[spring-security-oauth2-client-6.1.0.jar:6.1.0] at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:216) ~[spring-security-oauth2-client-6.1.0.jar:6.1.0] ... 178 common frames omittedyaml 의 설정을 제거 하고 Java Config 에서 등록을 하면 @Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(this.localRegistration()); } private ClientRegistration localRegistration() { return ClientRegistration.withRegistrationId("local") .clientId("google-client-id")순한 참조 예외가 발생합니다.Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'clientRegistrationRepository': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355) ~[spring-beans-6.0.9.jar:6.0.9] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227) ~[spring-beans-6.0.9.jar:6.0.9]Spring Authorization Sever 와 충돌의 의심해보았는데 RegisteredClientRepository 로 등록하거 있어 원인을 아닌거 같고 ClientRegistrationRepository 을 등록하는 코드에 문제가 있는거 같이 문의 합니다.개발은 Spring Authorization 1.1 로 진행하고 있습니다. OIDC Logout Endpoint 기능이 필요하여 지난주에 Spring Authorization 1.0.2 -> 1.1 로 업그레이드 하였습니다.
- 미해결스프링 시큐리티 OAuth2
운영서버에서 활용할 때 적합한가요?
안녕하세요강의 잘 듣고 있습니다! 강의해주시는대로 소셜 로그인을 구현했을 때 운영 서버에서도 문제가 없을까요?시큐리티를 활용하는 방법이 너무 많아서, 강의의 내용만으로도 운영서버에 적합한지, 적합하지 않다면 어떤점을 보완해야 할지 궁금합니다!
- 미해결스프링 시큐리티 OAuth2
코드를 가지고 at를 요청하는 걸 custom 할 수 있나요?
프론트에서 코드를 백으로 보내면 해당 코드로 at 를 받아서 유저 정보를 가져오고 싶은데 이렇게 하는게 가능한가요?
- 미해결스프링 시큐리티 OAuth2
oauth2Login() vs oauth2Client() 선택 고민입니다.
안녕하세요. SpringSecurity를 공식문서만으로 공부하기 벅차서 강의를 듣고있는데 도움을 많이 받고있습니다. 회사에서 인증서버를 구현하려고 하는데 요구사항이 고객사의 인가서버에서 OAuth2.0로 User정보를 회사 인증서버로 가져와서 그 User정보를 토대로 JWT토큰을 생성해서 회사 인증서버 자체에서 인증/권한 관리를 따로 하려고하는데 이런 경우 oauth2Client()를 써야하는게 맞는거죠?
- 미해결스프링 시큐리티 OAuth2
[OAuth 2.0 Social Login 연동 구현 3] 에서 계속 index 문자열만 보이고 index 페이지가 보이지 않습니다.
server: port: 8081 spring: security: oauth2: client: registration: keycloak: authorization-grant-type: authorization_code client-id: oauth2-client-app client-name: oauth2-client-app client-secret: dXf021lMWuZ9kZafqxZn230MvVEdROIo redirect-uri: http://localhost:8081/login/oauth2/code/keycloak scope: profile,email google: client-id: 발급받은ID client-secret: 발급받은비밀번호 scope: profile,email naver: client-id: 발급받은ID client-secret: 발급받은비밀번호 authorization-grant-type: authorization_code client-name: naver-client-app redirect-uri: http://localhost:8081/login/oauth2/code/naver scope: profile,email provider: keycloak: authorization-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/auth issuer-uri: http://localhost:8080/realms/oauth2 jwk-set-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/certs token-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/token user-info-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/userinfo user-name-attribute: preferred_username 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 mvc: static-path-pattern: /static/** package springsecurityoauth2.demo.controller; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class IndexController { @GetMapping("/") public String index(Model model, Authentication authentication, @AuthenticationPrincipal OAuth2User oAuth2User) { OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken) authentication; if (oAuth2AuthenticationToken != null) { Map<String, Object> attributes = oAuth2User.getAttributes(); String name = (String) attributes.get("name"); // 네이버는 response 계층이 하나 더 있으므로 별도 처리 필요 if (oAuth2AuthenticationToken.getAuthorizedClientRegistrationId().equals("naver")) { Map<String, Object> response = (Map<String, Object>) attributes.get("response"); name = (String) response.get("name"); } model.addAttribute("user", name); } return "index"; } } 안녕하세요.resource 파일들은 깃헙의 소셜로그인 브랜치에서 그대로 가져왔고, IndexController 와 application.yml 파일은 위와 같습니다. 브라우저에서 localhost:8081 로 접속하면이렇게만 나옵니다 ㅠㅠ어디가 잘못됐을까요?
- 미해결스프링 시큐리티 OAuth2
Spring security 6 이후 client-authentication-method 설정 방법 공유
yml에client-authentication-method: POST로 설정하면 아마 안될 것 입니다.ㅠㅠclient-authentication-method: client_secret_post로 설정하면 해결됩니다..이것 때문에 한참 고생했네요.....
- 미해결스프링 시큐리티 OAuth2
MSA 방식의 구조
현재 예제로 구성된 방식은 MSA방식이라고 생각해도 될까요? 자원서버 ( 앨범 , 친구 ) , 클라이언트서버 , 인가서버 형식으로 각각 서버를 나누어서 역할이 구분되어 있는방식이라 제가 이해한것이 맞는지 궁금합니다. 항상 좋은강의 해주셔서 감사합니다
- 미해결스프링 시큐리티 OAuth2
리소스 서버와 인가 서버가 동일한 예시
안녕하세요? 먼저 좋은 강의 감사드립니다. 실무에 많은 도움을 받고 있습니다.OAuth2.0 Roles(Resource Owner, Resource Server, Authorization Server, Client)의 개념에 대해 궁금한 점이 생겨서 아래 예시와 함께 질문드립니다.카카오톡과 연동한 게임을 보면 게임내 순위 화면에 카카오톡의 프로필 사진이 사용되는 것을 알 수 있는데요.이 경우 카카오톡의 OAuth2.0 인가 서버가 사용자 인증의 역할과 함께 리소스 서버(프로필 사진 제공)의 역할도 함께 수행하는 것으로 보이는데요 이게 맞을까요?감사합니다.
- 해결됨스프링 시큐리티 OAuth2
모바일앱의 인증을 위해 자체 인가서버와 소셜로그인을 함께 사용하는 경우
우선 좋은 강의 감사하다는 말씀드립니다. 올려주신 강의 2편 모두 잘 보고 있고 실무에서 많은 도움을 받고 있습니다.강사님께 배운 내용을 토대로 인증시스템을 구성해보려던 중 질문 사항이 있어 질문드립니다. 강의에서는 웹애플리케이션 기반 인증에 비중을 두고 설명을 잘 해주셨는데, 모바일 애플리케이션에 대상으로 인증을 구성을 해보려하니 조금 헷갈리는 부분이 있네요.요구사항은 아래와 같은데요,추후 내부 리소스를 외부서비스에 제공하기 위한 자체 OAuth2.0인가 서버 구성모바일 애플리케이션에서 JWT기반 인증을 할 수 있는 인증 및 회원 가입 API자체 OAuth2 인가서버와 연계하여 구성되는 리소스서버 구성소셜로그인 기능인증 시스템을 구성하기 위해 아래와 같은 방법을 생각하고 있습니다.자체 인증 시스템을 통한 로그인의 경우 자체 모바일앱에 별도의 web view를 생성하고 싶지 않으므로 API로 로그인 정보를 바로 받아서 내부적으로 Resource Owner Password Flow 처리하여 토큰 반환유저가 모바일앱 UI에 OAuth2 인가서버 계정정보(ID/PASSWORD)를 직접 입력로그인 버튼 클릭 시, 서버가 계정 정보를 API로 직접 전달받아 내부적으로 Resource Owner Password Flow를 통해 검증하고 토큰 및 인증 필요 정보를 클라이언트에 반환소셜로그인의 경우, 안전한 인증을 제공하기 위해 모바일 UI에 별도의 webView를 띄우고 Authorization Code Grant방식으로 인증진행webView를 통해 외부서비스에 대한 Authorization Code Grant 흐름이 진행.최종적으로 서버가 리다이렉트 주소를 통해 code를 전달받아 소셜 인증이 완료되는 경우(최초 회원인 경우 가입 처리하고), 서버는 자체 리소스서버에서 검증할 수 있는 Access Token을 따로 발급하여 응답하고 이를 모바일앱이 수신하여 인증처리. 질문은 아래와 같습니다.OAuth2.0 인가서버를 운용하는 경우 자사 모바일 앱의 자체로그인을 Resource Owner Password Flow로 직접 처리하는 것이 일반적인 방법인지? 더 좋은 방법은 없는지?외부 소셜 로그인의 경우 패스워드 없이 가입처리를 해야하는데 자체인가서버의 회원과 어떻게 일괄적으로 관리가 가능할지?외부 소셜 로그인의 경우라도 최종 발급되는 토큰은 자체 리소스서버에서 검증가능하도록 자체로그인과 일괄된 방법으로 JWT 발급이 되어야 할 것 같은데, Spring Authorization Server를 통해 효율적으로 구성이 가능할지?소셜로그인의 경우 서버가 리다이렉트 주소를 통해 code를 받아오더라도 OAuth2AuthorizedClient까지만 확인하고 세션에 인증정보를 저장하고 싶지 않은데 어떻게 효율적으로 custom 가능할지?oauth2Client API 및 OAuth2AuthorizedClientManager를 통해 토큰을 받아오는 것을 생각하고 있는데 더 합리적인 방법은 없을지?긴 질문 읽어주셔서 감사합니다. 질문가지수가 많아서 죄송하네요. 답변 주실 수 있는 부분까지만 주셔도 괜찮습니다.혹시 제가 구상하는 방법에서 개선되어야 하는 부분이나 혹시나 제가 잘못 생각하고 있는 부분은 없을 지 이런 부분도 조언해주시면 너무 감사하겠습니다.
- 미해결스프링 시큐리티 OAuth2
소셜로그인 + Jwt 인증 어떻게 하나요?
소셜로그인 성공 이후 JWT 인증하는 방법을 알고 싶어 강의를 수강했습니다.소셜로그인과 JWT 인증을 어떻게 같이 쓰는지 예시를 알려 주실 수 있나요실무에 바로 적용하고 싶은데 강의에서 보여 소셜로그인, JWT 인증 강의만으론 소셜로그인 성공 이후 어떻게 토큰을 발급해 react 또는 모바일 네이티브 앱의 사용자을 로그인, 회원가입시키는지 모르겠네요.샘플 코드를 첨부해주시면 감사하겠습니다.
- 미해결스프링 시큐리티 OAuth2
oauth2clinet api 사용 이유?
시큐리티 필터체인 구성 시 oauth2login api를 사용하면 되는데 굳이 oauth2client를 사용할 이유가 어떤것이 있을까요?
- 미해결스프링 시큐리티 OAuth2
0auth2.0 roles의 이해
키 클락을 로컬 8080으로 띄워서 강의처럼 실습을 하고 있습니다roles1번 을 통해 user 로 로그인 바로 리다이렉트 http:localhost:8081 로 넘어가고grant access 화면이 뜨지 않는데 왜 그런건가요 ?
- 미해결스프링 시큐리티 OAuth2
질문 사항
17:30 ppt를 보면 back chaennel에서 '클라이언트가 최종 사용자를 가지고 있는가?' 에 대한 말씀을 하시면서 '클라이언트가 사용자인 동시에 클라이언트의 역할을 수행하는 경우를 말한다'고 하셨는데 '아니오'일 때 Client Credentials Grant Type의 방식을 사용하게 되는 것이 이해가 잘 가지 않습니다.22:10'token, id_token의 경우 권한 부여 유형에서 지원해야 한다.' 라고 하셨는데 해당 방식의 경우 인가 서버의 구현 여부에 따라 사용할 수 있는지가 달라지는걸 말씀 하신건가요?25:25임시 코드 요청시와 액세스 토큰 요청 시에 같은 uri를 보내야 한다고 하셨는데, 이 값은 리소스 서버에 등록되어 있는 클라이언트의 redirect_uri 값과 항상 동일해야 하나요? 서버에 등록된 값과 다를 수 있는지 궁금합니다.
- 미해결스프링 시큐리티 OAuth2
Spring Authorization Server 1.0.1 state 문의
authorization_code grantType 으로 인가를 진행한다고 가 정하였을때 authorization_code 발급 요청에서 사용한 state 와 token 발급 요청해서 사용하는 state가 다르면 token 발급이 실패 해야 하는데 성공하고 있어서 분석해보니 oauth2_authorization 테이블의 state 필드가 null 로 저장되고 있습니다. JdbcOAuth2AuthorizationService를 사용하지 않고 커스텀한 구현체를 만들어서 사용하고 있는것이 원인일 수 있어 디버깅 해보니 OAuth2Authorization 의 소스를 분석해보면 state 가 포함되고 있지 않아서 저장이 안되고 있는데 아직 Spring Authorization Server는 state 가 구현되지 않은 걸까요? 추가로 소스를 분석해보니 nonce 도 아직 구현되지 않은거 같습니다.Spring Authorization Server 1.0.1 의 OAuth2Authorization 소스package org.springframework.security.oauth2.server.authorization; import java.io.Serializable; import java.time.Instant; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; import org.springframework.lang.Nullable; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.core.OAuth2Token; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.util.SpringAuthorizationServerVersion; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** * A representation of an OAuth 2.0 Authorization, which holds state related to the authorization granted * to a {@link #getRegisteredClientId() client}, by the {@link #getPrincipalName() resource owner} * or itself in the case of the {@code client_credentials} grant type. * * @author Joe Grandja * @author Krisztian Toth * @since 0.0.1 * @see RegisteredClient * @see AuthorizationGrantType * @see OAuth2Token * @see OAuth2AccessToken * @see OAuth2RefreshToken */ public class OAuth2Authorization implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; private String id; private String registeredClientId; private String principalName; private AuthorizationGrantType authorizationGrantType; private Set<String> authorizedScopes; private Map<Class<? extends OAuth2Token>, Token<?>> tokens; private Map<String, Object> attributes; protected OAuth2Authorization() { } /** * Returns the identifier for the authorization. * * @return the identifier for the authorization */ public String getId() { return this.id; } /** * Returns the identifier for the {@link RegisteredClient#getId() registered client}. * * @return the {@link RegisteredClient#getId()} */ public String getRegisteredClientId() { return this.registeredClientId; } /** * Returns the {@code Principal} name of the resource owner (or client). * * @return the {@code Principal} name of the resource owner (or client) */ public String getPrincipalName() { return this.principalName; } /** * Returns the {@link AuthorizationGrantType authorization grant type} used for the authorization. * * @return the {@link AuthorizationGrantType} used for the authorization */ public AuthorizationGrantType getAuthorizationGrantType() { return this.authorizationGrantType; } /** * Returns the authorized scope(s). * * @return the {@code Set} of authorized scope(s) * @since 0.4.0 */ public Set<String> getAuthorizedScopes() { return this.authorizedScopes; } /** * Returns the {@link Token} of type {@link OAuth2AccessToken}. * * @return the {@link Token} of type {@link OAuth2AccessToken} */ public Token<OAuth2AccessToken> getAccessToken() { return getToken(OAuth2AccessToken.class); } /** * Returns the {@link Token} of type {@link OAuth2RefreshToken}. * * @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if not available */ @Nullable public Token<OAuth2RefreshToken> getRefreshToken() { return getToken(OAuth2RefreshToken.class); } /** * Returns the {@link Token} of type {@code tokenType}. * * @param tokenType the token type * @param <T> the type of the token * @return the {@link Token}, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T extends OAuth2Token> Token<T> getToken(Class<T> tokenType) { Assert.notNull(tokenType, "tokenType cannot be null"); Token<?> token = this.tokens.get(tokenType); return token != null ? (Token<T>) token : null; } /** * Returns the {@link Token} matching the {@code tokenValue}. * * @param tokenValue the token value * @param <T> the type of the token * @return the {@link Token}, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T extends OAuth2Token> Token<T> getToken(String tokenValue) { Assert.hasText(tokenValue, "tokenValue cannot be empty"); for (Token<?> token : this.tokens.values()) { if (token.getToken().getTokenValue().equals(tokenValue)) { return (Token<T>) token; } } return null; } /** * Returns the attribute(s) associated to the authorization. * * @return a {@code Map} of the attribute(s) */ public Map<String, Object> getAttributes() { return this.attributes; } /** * Returns the value of an attribute associated to the authorization. * * @param name the name of the attribute * @param <T> the type of the attribute * @return the value of an attribute associated to the authorization, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T> T getAttribute(String name) { Assert.hasText(name, "name cannot be empty"); return (T) this.attributes.get(name); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } OAuth2Authorization that = (OAuth2Authorization) obj; return Objects.equals(this.id, that.id) && Objects.equals(this.registeredClientId, that.registeredClientId) && Objects.equals(this.principalName, that.principalName) && Objects.equals(this.authorizationGrantType, that.authorizationGrantType) && Objects.equals(this.authorizedScopes, that.authorizedScopes) && Objects.equals(this.tokens, that.tokens) && Objects.equals(this.attributes, that.attributes); } @Override public int hashCode() { return Objects.hash(this.id, this.registeredClientId, this.principalName, this.authorizationGrantType, this.authorizedScopes, this.tokens, this.attributes); } /** * Returns a new {@link Builder}, initialized with the provided {@link RegisteredClient#getId()}. * * @param registeredClient the {@link RegisteredClient} * @return the {@link Builder} */ public static Builder withRegisteredClient(RegisteredClient registeredClient) { Assert.notNull(registeredClient, "registeredClient cannot be null"); return new Builder(registeredClient.getId()); } /** * Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2Authorization}. * * @param authorization the {@code OAuth2Authorization} used for initializing the {@link Builder} * @return the {@link Builder} */ public static Builder from(OAuth2Authorization authorization) { Assert.notNull(authorization, "authorization cannot be null"); return new Builder(authorization.getRegisteredClientId()) .id(authorization.getId()) .principalName(authorization.getPrincipalName()) .authorizationGrantType(authorization.getAuthorizationGrantType()) .authorizedScopes(authorization.getAuthorizedScopes()) .tokens(authorization.tokens) .attributes(attrs -> attrs.putAll(authorization.getAttributes())); } /** * A holder of an OAuth 2.0 Token and it's associated metadata. * * @author Joe Grandja * @since 0.1.0 */ public static class Token<T extends OAuth2Token> implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; protected static final String TOKEN_METADATA_NAMESPACE = "metadata.token."; /** * The name of the metadata that indicates if the token has been invalidated. */ public static final String INVALIDATED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("invalidated"); /** * The name of the metadata used for the claims of the token. */ public static final String CLAIMS_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("claims"); private final T token; private final Map<String, Object> metadata; protected Token(T token) { this(token, defaultMetadata()); } protected Token(T token, Map<String, Object> metadata) { this.token = token; this.metadata = Collections.unmodifiableMap(metadata); } /** * Returns the token of type {@link OAuth2Token}. * * @return the token of type {@link OAuth2Token} */ public T getToken() { return this.token; } /** * Returns {@code true} if the token has been invalidated (e.g. revoked). * The default is {@code false}. * * @return {@code true} if the token has been invalidated, {@code false} otherwise */ public boolean isInvalidated() { return Boolean.TRUE.equals(getMetadata(INVALIDATED_METADATA_NAME)); } /** * Returns {@code true} if the token has expired. * * @return {@code true} if the token has expired, {@code false} otherwise */ public boolean isExpired() { return getToken().getExpiresAt() != null && Instant.now().isAfter(getToken().getExpiresAt()); } /** * Returns {@code true} if the token is before the time it can be used. * * @return {@code true} if the token is before the time it can be used, {@code false} otherwise */ public boolean isBeforeUse() { Instant notBefore = null; if (!CollectionUtils.isEmpty(getClaims())) { notBefore = (Instant) getClaims().get("nbf"); } return notBefore != null && Instant.now().isBefore(notBefore); } /** * Returns {@code true} if the token is currently active. * * @return {@code true} if the token is currently active, {@code false} otherwise */ public boolean isActive() { return !isInvalidated() && !isExpired() && !isBeforeUse(); } /** * Returns the claims associated to the token. * * @return a {@code Map} of the claims, or {@code null} if not available */ @Nullable public Map<String, Object> getClaims() { return getMetadata(CLAIMS_METADATA_NAME); } /** * Returns the value of the metadata associated to the token. * * @param name the name of the metadata * @param <V> the value type of the metadata * @return the value of the metadata, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <V> V getMetadata(String name) { Assert.hasText(name, "name cannot be empty"); return (V) this.metadata.get(name); } /** * Returns the metadata associated to the token. * * @return a {@code Map} of the metadata */ public Map<String, Object> getMetadata() { return this.metadata; } protected static Map<String, Object> defaultMetadata() { Map<String, Object> metadata = new HashMap<>(); metadata.put(INVALIDATED_METADATA_NAME, false); return metadata; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Token<?> that = (Token<?>) obj; return Objects.equals(this.token, that.token) && Objects.equals(this.metadata, that.metadata); } @Override public int hashCode() { return Objects.hash(this.token, this.metadata); } } /** * A builder for {@link OAuth2Authorization}. */ public static class Builder implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; private String id; private final String registeredClientId; private String principalName; private AuthorizationGrantType authorizationGrantType; private Set<String> authorizedScopes; private Map<Class<? extends OAuth2Token>, Token<?>> tokens = new HashMap<>(); private final Map<String, Object> attributes = new HashMap<>(); protected Builder(String registeredClientId) { this.registeredClientId = registeredClientId; } /** * Sets the identifier for the authorization. * * @param id the identifier for the authorization * @return the {@link Builder} */ public Builder id(String id) { this.id = id; return this; } /** * Sets the {@code Principal} name of the resource owner (or client). * * @param principalName the {@code Principal} name of the resource owner (or client) * @return the {@link Builder} */ public Builder principalName(String principalName) { this.principalName = principalName; return this; } /** * Sets the {@link AuthorizationGrantType authorization grant type} used for the authorization. * * @param authorizationGrantType the {@link AuthorizationGrantType} * @return the {@link Builder} */ public Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) { this.authorizationGrantType = authorizationGrantType; return this; } /** * Sets the authorized scope(s). * * @param authorizedScopes the {@code Set} of authorized scope(s) * @return the {@link Builder} * @since 0.4.0 */ public Builder authorizedScopes(Set<String> authorizedScopes) { this.authorizedScopes = authorizedScopes; return this; } /** * Sets the {@link OAuth2AccessToken access token}. * * @param accessToken the {@link OAuth2AccessToken} * @return the {@link Builder} */ public Builder accessToken(OAuth2AccessToken accessToken) { return token(accessToken); } /** * Sets the {@link OAuth2RefreshToken refresh token}. * * @param refreshToken the {@link OAuth2RefreshToken} * @return the {@link Builder} */ public Builder refreshToken(OAuth2RefreshToken refreshToken) { return token(refreshToken); } /** * Sets the {@link OAuth2Token token}. * * @param token the token * @param <T> the type of the token * @return the {@link Builder} */ public <T extends OAuth2Token> Builder token(T token) { return token(token, (metadata) -> {}); } /** * Sets the {@link OAuth2Token token} and associated metadata. * * @param token the token * @param metadataConsumer a {@code Consumer} of the metadata {@code Map} * @param <T> the type of the token * @return the {@link Builder} */ public <T extends OAuth2Token> Builder token(T token, Consumer<Map<String, Object>> metadataConsumer) { Assert.notNull(token, "token cannot be null"); Map<String, Object> metadata = Token.defaultMetadata(); Token<?> existingToken = this.tokens.get(token.getClass()); if (existingToken != null) { metadata.putAll(existingToken.getMetadata()); } metadataConsumer.accept(metadata); Class<? extends OAuth2Token> tokenClass = token.getClass(); this.tokens.put(tokenClass, new Token<>(token, metadata)); return this; } protected final Builder tokens(Map<Class<? extends OAuth2Token>, Token<?>> tokens) { this.tokens = new HashMap<>(tokens); return this; } /** * Adds an attribute associated to the authorization. * * @param name the name of the attribute * @param value the value of the attribute * @return the {@link Builder} */ public Builder attribute(String name, Object value) { Assert.hasText(name, "name cannot be empty"); Assert.notNull(value, "value cannot be null"); this.attributes.put(name, value); return this; } /** * A {@code Consumer} of the attributes {@code Map} * allowing the ability to add, replace, or remove. * * @param attributesConsumer a {@link Consumer} of the attributes {@code Map} * @return the {@link Builder} */ public Builder attributes(Consumer<Map<String, Object>> attributesConsumer) { attributesConsumer.accept(this.attributes); return this; } /** * Builds a new {@link OAuth2Authorization}. * * @return the {@link OAuth2Authorization} */ public OAuth2Authorization build() { Assert.hasText(this.principalName, "principalName cannot be empty"); Assert.notNull(this.authorizationGrantType, "authorizationGrantType cannot be null"); OAuth2Authorization authorization = new OAuth2Authorization(); if (!StringUtils.hasText(this.id)) { this.id = UUID.randomUUID().toString(); } authorization.id = this.id; authorization.registeredClientId = this.registeredClientId; authorization.principalName = this.principalName; authorization.authorizationGrantType = this.authorizationGrantType; authorization.authorizedScopes = Collections.unmodifiableSet( !CollectionUtils.isEmpty(this.authorizedScopes) ? new HashSet<>(this.authorizedScopes) : new HashSet<>() ); authorization.tokens = Collections.unmodifiableMap(this.tokens); authorization.attributes = Collections.unmodifiableMap(this.attributes); return authorization; } } }
- 미해결스프링 시큐리티 OAuth2
로그아웃관련하여 질문드립니다!
안녕하세요!제가 강의를 듣고 직접 구현을 해보고 있는 중에 궁금한 점이 있습니다. 저는 인가서버에다가 로그인 페이지를 구성해서 아이디와 비밀번호를 입력하면 동의화면이 나와서 SCOPE 를 부여한 뒤 리소스 서버에 접근하는 로직의 흐름으로 구현을 생각해보고 있습니다.로그아웃을 하고나면 인가서버를 통해서 동의 받은 SCOPE도 다 날려서 인가서버 로그인화면으로 리다이렉트 하고 싶습니다. 제 바람과는 달리 계속 이미 인가가 된 상태가 되어서 마지막 강의 예제 코드를 기반으로 연구하고 있는데 127.0.0.1:8081/home 에서 로그아웃을 하고난 뒤 127.0.0.1:8081 로 리다이렉트 되도록 했는데 로그인링크를 누르면 127.0.0.1:9000/login 으로 가는 것이 아니라 바로 127.0.0.1:8081 로 이동해서 access token 버튼이 있는 화면으로 넘어갑니다. 제가 질문드리고 싶은 것은 로그아웃을 하고나서 로그인 하면 인가서버에 구현된 로그인 화면으로 이동해서 다시 로그인해서 동의화면을 받도록 하려면 어떤식으로 로직을 구성해야 할까요?ㅠ 몇 주 고민하다가 방법을 모르겠어서 질문드립니다.
- 미해결스프링 시큐리티 OAuth2
사용자 관리에 대해서 문의드립니다.
강의에서는 네이버나 구글을 통해 로그인을 진행하면 access token 을 받고 이를 통해 사용자정보를 받아오면 이를 인증완료로 보는 것 같은데요.일반적으로는 받아온 사용자정보를 가지고 OAuth Client 자체 DB 에 사용자마스터를 등록하고 별도의 사용자 식별키 관리를 해야하지 않는지요? 이러한 처리가 생략되어 있는 것 같은데 이 부분이 궁금합니다.
- 미해결스프링 시큐리티 OAuth2
정수원님이 기술을 학습하시는 방법이 궁금합니다
안녕하세요 정수원님 우선 좋은 강의 만들어주셔서 감사합니다.강의와는 조금 다른 질문이지만 듣다보니 궁금한점이 생겨서 질문드립니다.예를 들어 Spring Security를 처음 학습하신다고 하면 어떤식으로 학습하시고, 내부구조를 디버깅해보시는지 궁금합니다.강좌에서보면 특정 클래스들에 break point를 걸고 디버깅하시는 모습을 보여주십니다.하지만 처음 Spring Security를 학습한다고 가정하면 어떤 클래스들이 핵심 역할을 하는지 파악하기 힘들것 같다고 느껴졌습니다. 개인적인 생각으로는 2가지 방법을 생각해 보았습니다.Spring Security Docs를 보고 주요 클래스들을 파악해본다WebMvcAutoConfiguration, SecurityAutoConfiguration등 클래스 하위로 파악해나간다 이에 대해 수원님은 어떤 방식으로 접근하시는지 궁금해서 질문 드립니다