diff --git a/authorization-server/build.gradle b/authorization-server/build.gradle index 9d534ca..ff55428 100644 --- a/authorization-server/build.gradle +++ b/authorization-server/build.gradle @@ -24,6 +24,9 @@ ext { } dependencies { + compile('org.springframework.security:spring-security-oauth2-client') + compile('org.springframework.security:spring-security-oauth2-jose') + implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-freemarker' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java index 0f858ea..a658dd6 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java @@ -1,16 +1,29 @@ package io.bluemoon.authorizationserver.config; +import io.bluemoon.authorizationserver.domain.social.UserArgumentResolver; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.web.filter.ForwardedHeaderFilter; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.util.List; + @EnableWebSecurity public class WebMvcConfig implements WebMvcConfigurer { + @Autowired + private UserArgumentResolver userArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(userArgumentResolver); + } + @Bean FilterRegistrationBean forwardedHeaderFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java index 9fbd2c9..7f2b20b 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java @@ -1,11 +1,6 @@ package io.bluemoon.authorizationserver.config; -import io.bluemoon.authorizationserver.domain.social.ClientResources; import io.bluemoon.authorizationserver.service.user.CustomUserDetailsServiceImpl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -16,28 +11,18 @@ 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.NoOpPasswordEncoder; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.web.filter.CharacterEncodingFilter; -import javax.servlet.Filter; - @Configuration @EnableWebSecurity //@Order(SecurityProperties.BASIC_AUTH_ORDER - 6) -@EnableOAuth2Client @Order(-1) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private CustomUserDetailsServiceImpl customUserDetailsService; - @Qualifier("oauth2ClientContext") - @Autowired - private OAuth2ClientContext oAuth2ClientContext; - public WebSecurityConfig( CustomUserDetailsServiceImpl customUserDetailsService ) { @@ -63,8 +48,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { http .authorizeRequests() .antMatchers("/", "/login/**", "/css/**", "/images/**", "/js/**", - "/console/**").permitAll() + "/console/**", "/oauth2/**").permitAll() .anyRequest().authenticated() + .and() + .oauth2Login() + .defaultSuccessUrl("/loginSuccess") + .failureUrl("/loginFailure") .and() .headers().frameOptions().disable() .and() @@ -78,8 +67,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .deleteCookies("JSESSSIONID") .invalidateHttpSession(true) .and() - .addFilterBefore(filter, CsrfFilter.class) - .csrf().disable(); + .addFilterBefore(filter, CsrfFilter.class); +// .csrf().disable(); // http.formLogin().loginPage("/login").permitAll() // .and() // .requestMatchers().antMatchers("/login", "/logout", "/oauth/authorize", "/oauth/confirm_access") @@ -121,12 +110,42 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { // social login - - - @Bean - @ConfigurationProperties("facebook") - public ClientResources facebook() { - return new ClientResources(); - } +// @Bean +// public FilterRegistrationBean oauth2ClientFilterRegistration( +// OAuth2ClientContextFilter filter +// ) { +// FilterRegistrationBean registration = new FilterRegistrationBean(); +// registration.setFilter(new ForwardedHeaderFilter()); +// registration.setFilter(filter); +// registration.setOrder(-100); +// return registration; +// } +// +// private Filter oauth2Filter() { +// CompositeFilter filter = new CompositeFilter(); +// List filters = new ArrayList<>(); +// filters.add(oauth2Filter(facebook(), "/login/facebook", SocialType.FACEBOOK)); +// +// filter.setFilters(filters); +// return filter; +// } +// +// private Filter oauth2Filter(ClientResources client, String path, SocialType socialType) { +// OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path); +// OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oAuth2ClientContext); +// filter.setRestTemplate(template); +// filter.setTokenServices(new UserTokenService(client, socialType)); +// filter.setAuthenticationSuccessHandler((request, response, authentication) -> +// response.sendRedirect("/" + socialType.getVaule() + "/complete")); +// filter.setAuthenticationFailureHandler((request, response, exception) -> +// response.sendRedirect("/error")); +// return filter; +// } +// +// @Bean +// @ConfigurationProperties("facebook") +// public ClientResources facebook() { +// return new ClientResources(); +// } } diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/annotation/SocialUser.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/annotation/SocialUser.java new file mode 100644 index 0000000..9db90b4 --- /dev/null +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/annotation/SocialUser.java @@ -0,0 +1,12 @@ +package io.bluemoon.authorizationserver.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface SocialUser { + +} diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/controller/sso/SsoController.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/controller/sso/SsoController.java index c19c32e..b7c781d 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/controller/sso/SsoController.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/controller/sso/SsoController.java @@ -1,17 +1,23 @@ package io.bluemoon.authorizationserver.controller.sso; +import io.bluemoon.authorizationserver.config.annotation.SocialUser; import io.bluemoon.authorizationserver.domain.UserResponseWrapper; import io.bluemoon.authorizationserver.domain.oauth.accesstoken.AccessToken; +import io.bluemoon.authorizationserver.domain.social.SocialType; +import io.bluemoon.authorizationserver.domain.user.User; import io.bluemoon.authorizationserver.service.sso.SsoService; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import java.security.Principal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; @Controller public class SsoController { @@ -75,4 +81,34 @@ public class SsoController { return user; } + @GetMapping(value = "/loginSuccess") + public String loginComplete(@SocialUser User user) { + return "redirect:/login/success"; + } + + @GetMapping(value = "/login/success") + @ResponseBody + public String test2() { + return "kkk"; + } + + + + +// @GetMapping(value = "/{facebook|google|kakao}/complete") +// public String loginComplete(HttpSession session) { +// OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); +// Map map = (HashMap) authentication.getUserAuthentication().getDetails(); +// session.setAttribute("user", User.builder() +// .username(map.get("username")) +// .email(map.get("email")) +// .principal(map.get("id")) +// .socialType(SocialType.FACEBOOK) +// .createdAt(LocalDateTime.now()) +// .updatedAt(LocalDateTime.now()) +// .build() +// ); +// return "redirect:/"; +// } + } diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/ClientResources.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/ClientResources.java index d49fe99..1a09e30 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/ClientResources.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/ClientResources.java @@ -1,22 +1,22 @@ -package io.bluemoon.authorizationserver.domain.social; - -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; - -public class ClientResources { - - @NestedConfigurationProperty - private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails(); - - @NestedConfigurationProperty - private ResourceServerProperties resource = new ResourceServerProperties(); - - public AuthorizationCodeResourceDetails getClient() { - return client; - } - - private ResourceServerProperties getResource() { - return resource; - } -} +//package io.bluemoon.authorizationserver.domain.social; +// +//import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; +//import org.springframework.boot.context.properties.NestedConfigurationProperty; +//import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +// +//public class ClientResources { +// +// @NestedConfigurationProperty +// private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails(); +// +// @NestedConfigurationProperty +// private ResourceServerProperties resource = new ResourceServerProperties(); +// +// public AuthorizationCodeResourceDetails getClient() { +// return client; +// } +// +// public ResourceServerProperties getResource() { +// return resource; +// } +//} diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserArgumentResolver.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserArgumentResolver.java new file mode 100644 index 0000000..05b95c7 --- /dev/null +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserArgumentResolver.java @@ -0,0 +1,127 @@ +package io.bluemoon.authorizationserver.domain.social; + +import io.bluemoon.authorizationserver.config.annotation.SocialUser; +import io.bluemoon.authorizationserver.domain.user.User; +import io.bluemoon.authorizationserver.domain.user.UserRepository; +import org.springframework.core.MethodParameter; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +@Component +public class UserArgumentResolver implements HandlerMethodArgumentResolver { + + private UserRepository userRepository; + + public UserArgumentResolver(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterAnnotation(SocialUser.class) != null && + parameter.getParameterType().equals(User.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession(); + User user = (User) session.getAttribute("user"); + return getUser(user, session); + } + + /** + * 인증된 User 객체를 만드는 메인 메서드 + * @param user + * @param session + * @return + */ + private User getUser(User user, HttpSession session) { + // 세션에서 가져온 유저가 널일 경우에만 + System.out.println("-------------------------------------"); + System.out.println(user); + if (user == null) { + try { +// OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); +// Map map = (HashMap) authentication.getUserAuthentication().getDetails(); +// User convertUser = convertUser(String.valueOf(authentication.getAuthorities().toArray()[0]), map); + OAuth2AuthenticationToken authentication = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + Map map = authentication.getPrincipal().getAttributes(); + User convertUser = convertUser(authentication.getAuthorizedClientRegistrationId(), map); + + user = userRepository.findByEmail(convertUser.getEmail()); + if (user == null) { + user = userRepository.save(convertUser); + } + setRoleIfNotSame(user, authentication, map); + session.setAttribute("user", user); + } catch (ClassCastException e) { + return user; + } + } + System.out.println(user); + return user; + } + + /** + * 사용자의 인증된 소셜 미디어 타입에 따라 빌더를 사용하여 User 객체를 만들어 주는 가교 역할 + * @param authority + * @param map + * @return + */ + private User convertUser(String authority, Map map) { + if (SocialType.FACEBOOK.getVaule().equals(authority)) return getModernUser(SocialType.FACEBOOK, map); + + return null; + } + + /** + * 페이스북이나 구글 같이 공통되는 명명규칙을 가진 그룹을 맵핑 + * @param socialType + * @param map + * @return + */ + private User getModernUser(SocialType socialType, Map map) { + return User.builder() + .username(String.valueOf(map.get("name"))) + .email(String.valueOf(map.get("mail"))) + .principal(String.valueOf(map.get("id"))) + .socialType(socialType) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + } + + /** + * 인증된 authentication이 권한을 갖고 있는지 체크하는 용도 + * 만약 저장된 User 권한이 없으면 SecurityContextHolder를 사용하여 해당 소셜미디어 타입으로 권한을 저장 + * @param user + * @param authentication + * @param map + */ + private void setRoleIfNotSame(User user, OAuth2AuthenticationToken authentication, Map map) { + if (!authentication.getAuthorities().contains( + new SimpleGrantedAuthority(user.getSocialType().getRoleType()))) { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken(map, "N/A", AuthorityUtils.createAuthorityList(user.getSocialType().getRoleType())) + ); + } + } + + +} diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserTokenService.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserTokenService.java new file mode 100644 index 0000000..c7bd315 --- /dev/null +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/social/UserTokenService.java @@ -0,0 +1,31 @@ +//package io.bluemoon.authorizationserver.domain.social; +// +//import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; +//import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; +//import org.springframework.security.core.GrantedAuthority; +//import org.springframework.security.core.authority.AuthorityUtils; +// +//import java.util.List; +//import java.util.Map; +// +//public class UserTokenService extends UserInfoTokenServices { +// public UserTokenService(ClientResources resources, SocialType socialType) { +// super(resources.getResource().getUserInfoUri(), resources.getClient().getClientId()); +// +// setAuthoritiesExtractor(new OAuth2AuthoritiesExtractor(socialType)); +// +// } +// +// public static class OAuth2AuthoritiesExtractor implements AuthoritiesExtractor { +// private String socialType; +// +// public OAuth2AuthoritiesExtractor(SocialType socialType) { +// this.socialType = socialType.getRoleType(); +// } +// +// @Override +// public List extractAuthorities(Map map) { +// return AuthorityUtils.createAuthorityList(this.socialType); +// } +// } +//} diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserDetail.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserDetail.java index 7fb00ab..9138c70 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserDetail.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserDetail.java @@ -21,9 +21,9 @@ public class UserDetail implements UserDetails { public UserDetail(User user) { this.id = user.getId(); - this.username = user.getUserName(); + this.username = user.getUsername(); this.password = user.getPassword(); - this.userType = user.getUserType(); +// this.userType = user.getSocialType(); } @Override diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserRepository.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserRepository.java index 25b5c5e..c35058d 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserRepository.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/domain/user/UserRepository.java @@ -4,5 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; public interface UserRepository extends JpaRepository { - User findByUserName(String userName); + User findByUsername(String username); + User findByEmail(String email); } diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/service/user/CustomUserDetailsServiceImpl.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/service/user/CustomUserDetailsServiceImpl.java index e595e98..560a63b 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/service/user/CustomUserDetailsServiceImpl.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/service/user/CustomUserDetailsServiceImpl.java @@ -24,7 +24,7 @@ public class CustomUserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - User user = userRepository.findByUserName(username); + User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("UsernameNotFound[" + username + "]"); diff --git a/authorization-server/src/main/resources/application.properties b/authorization-server/src/main/resources/application.properties index a62f53a..4a4e115 100644 --- a/authorization-server/src/main/resources/application.properties +++ b/authorization-server/src/main/resources/application.properties @@ -1,5 +1,5 @@ server.port=8081 -server.servlet.context-path=/mk-auth +#server.servlet.context-path=/mk-auth server.use-forward-headers=false security.oauth2.authorization.check-token-access=isAuthenticated() @@ -18,12 +18,16 @@ spring.jpa.show-sql=true #spring.jpa.generate-ddl=false #spring.jpa.hibernate.ddl-auto=none -facebook.client.client-id=1684497291764010 -facebook.client.client-secret=cfefbfbb6ca436828f197df32d85b861 -facebook.client.access-token-uri=https://graph.facebook.com/oauth/access_token -facebook.client.user-authorization-uri=https://www.facebook.com/dialog/oauth -facebook.client.token-name=oauth_token -facebook.client.authentication-scheme=query -facebook.client.client-authentication-scheme=form +spring.security.oauth2.client.registration.facebook.client-id=715358882216622 +spring.security.oauth2.client.registration.facebook.client-secret=a39d8f1e06e8c3863d12e8461f4991e8 +#facebook.client.access-token-uri=https://graph.facebook.com/oauth/access_token +#facebook.client.user-authorization-uri=https://www.facebook.com/dialog/oauth +#facebook.client.token-name=oauth_token +#facebook.client.authentication-scheme=query +#facebook.client.client-authentication-scheme=form +# +#facebook.resource.user-info-uri=https://graph.facebook.com/me?fields=id -facebook.resource.user-info-uri=https://graph.facebook.com/me \ No newline at end of file + +logging.level.web=debug +spring.http.log-request-details=true \ No newline at end of file diff --git a/authorization-server/src/main/resources/templates/login.ftl b/authorization-server/src/main/resources/templates/login.ftl index dc39c82..75bddfb 100644 --- a/authorization-server/src/main/resources/templates/login.ftl +++ b/authorization-server/src/main/resources/templates/login.ftl @@ -16,5 +16,9 @@ + +