diff --git a/README.md b/README.md index 416da02b0fab274fb8cff39dfc333dbc086a34fc..d86ed906b05b87c4fb6a30b51488287a56cb5929 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ 项目用Maven管理, 字符编码: UTF-8 +授权协议:GPL-2.0 使用的技术与版本号
    @@ -17,22 +18,26 @@
  1. MySQL (5.6.23)
  2. EhCache (2.0.4)
+

技术视频

+http://list.youku.com/albumlist/show/id_51900110.html +(持续更新...) +
-Java Config版本请访问Branch: config +

Spring-Boot版本请访问Branch: config

-MongoDB版本请访问Branch: mongodb +MongoDB版本请访问Branch: mongodb OAuth2下一代身份认证授权协议OIDC实现: MyOIDC

- OAuth2客户端项目请访问 spring-oauth-client + OAuth2客户端项目请访问 spring-oauth-client

在线测试访问地址 http://andaily.com/spring-oauth-server/

- Shiro与OLTU整合的OAuth2项目 http://git.oschina.net/mkk/oauth2-shiro + Shiro与OLTU整合的OAuth2项目 https://gitee.com/mkk/oauth2-shiro
(相比spring-oauth-server, 该项目入门门槛相对较低, 代码更加透明, 理解更容易,可扩展性更强, 且模块化开发)


@@ -44,7 +49,7 @@ 项目是Maven管理的, 需要本地安装maven(开发用的maven版本号为3.1.0), 还有MySql(开发用的mysql版本号为5.6)
  • -下载(或clone)项目到本地 +下载(或clone)项目到本地
  • 创建MySQL数据库(如数据库名oauth2), 并运行相应的SQL脚本(脚本文件位于others/database目录), @@ -52,7 +57,7 @@ 运行脚本的顺序: initial_db.ddl -> oauth.ddl -> initial_data.ddl
  • -修改spring-oauth-server.properties(位于src/main/resources目录)中的数据库连接信息(包括username, password等) +修改spring-oauth-server.properties(位于src/main/resources目录)中的数据库连接信息(包括username, password等)
  • 将本地项目导入到IDE(如Intellij IDEA)中,配置Tomcat(或类似的servlet运行服务器), 并启动Tomcat(默认端口为8080); @@ -63,7 +68,7 @@ 将war放在Tomcat中并启动(注意: 这种方式需要将spring-oauth-server.properties加入到classpath中并正确配置数据库连接信息).
  • -参考oauth_test.txt(位于others目录)的内容并测试之(也可在浏览器中访问相应的地址,如: http://localhost:8080/spring-oauth-server). +参考oauth_test.txt(位于others目录)的内容并测试之(也可在浏览器中访问相应的地址,如: http://localhost:8080/spring-oauth-server).
  • @@ -93,8 +98,8 @@
  • - 如果在使用过程中遇到特殊的问题(如:如何将oauth_code存入数据库),请访问项目的 Wiki - 与 附件. + 如果在使用过程中遇到特殊的问题(如:如何将oauth_code存入数据库),请访问项目的 Wiki + 与 附件.
    我会把大家反馈的问题解决办法添加在这里.
    @@ -105,7 +110,7 @@

  • - 如果在使用项目的过程中发现任何的BUG或者更好的提议, 建议将其提交到项目的 Issues 中, + 如果在使用项目的过程中发现任何的BUG或者更好的提议, 建议将其提交到项目的 Issues 中, 我会一直关注并不断改进项目.

  • @@ -115,10 +120,10 @@ 功能扩展
    1. - oauth_code存入数据库的配置, 请下载文件 oauth_code存入数据库的配置.jpg + oauth_code存入数据库的配置, 请下载文件 oauth_code存入数据库的配置.jpg
    2. - 改变token过期的时间的配置, 请下载文件改变token过期的时间的配置.jpg + 改变token过期的时间的配置, 请下载文件改变token过期的时间的配置.jpg
    3. 自定义 grant_type, 默认情况支持的grant_type包括 [password,authorization_code,refresh_token,implicit], 若不需要其中的某些grant_type, @@ -152,14 +157,40 @@

      从 0.3版本开始将项目的所有计划的开发内容列出来, 方便大家跟进, 也欢迎你加入.
      -项目的开发管理使用开源项目 andaily-developer. +项目的开发管理使用开源项目 andaily-developer.

      diff --git a/src/main/java/com/monkeyk/sos/infrastructure/CacheConstants.java b/src/main/java/com/monkeyk/sos/infrastructure/CacheConstants.java index 3fe0f37004681b06ad288452189dfc62e86734e2..59b0f89c8e46f8fb257007da7cf6c55f10c4d22c 100644 --- a/src/main/java/com/monkeyk/sos/infrastructure/CacheConstants.java +++ b/src/main/java/com/monkeyk/sos/infrastructure/CacheConstants.java @@ -9,35 +9,32 @@ package com.monkeyk.sos.infrastructure; * * @author Shengzhao Li */ -public abstract class CacheConstants { +public interface CacheConstants { /** * client Details Cache, key is clientId */ - public static final String CLIENT_DETAILS_CACHE = "clientDetailsCache"; + String CLIENT_DETAILS_CACHE = "clientDetailsCache"; /** * access Token Cache, key is token */ - public static final String ACCESS_TOKEN_CACHE = "accessTokenCache"; + String ACCESS_TOKEN_CACHE = "accessTokenCache"; /** * refresh Token Cache, key is token */ - public static final String REFRESH_TOKEN_CACHE = "refreshTokenCache"; + String REFRESH_TOKEN_CACHE = "refreshTokenCache"; /** * authorization Code Cache, key is code */ - public static final String AUTHORIZATION_CODE_CACHE = "authorizationCodeCache"; + String AUTHORIZATION_CODE_CACHE = "authorizationCodeCache"; /** * user Cache, key is username */ - public static final String USER_CACHE = "userCache"; + String USER_CACHE = "userCache"; - private CacheConstants() { - } - } diff --git a/src/main/java/com/monkeyk/sos/infrastructure/SOSCacheUtils.java b/src/main/java/com/monkeyk/sos/infrastructure/SOSCacheUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..27073b1c42a326d76f7bb80206b303d8524e26d8 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/infrastructure/SOSCacheUtils.java @@ -0,0 +1,41 @@ +package com.monkeyk.sos.infrastructure; + +import com.monkeyk.sos.web.context.BeanProvider; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.util.Assert; + +/** + * 2018/10/14 + *

      + *

      + * Cache 操作相关 + * + * @author Shengzhao Li + * @since 1.0 + */ +public class SOSCacheUtils implements CacheConstants { + + + private SOSCacheUtils() { + } + + + /** + * 用户 Cache + * + * @return Cache instance + */ + public static Cache userCache() { + final CacheManager cacheManager = getCacheManager(); + return cacheManager.getCache(USER_CACHE); + } + + private static CacheManager getCacheManager() { + final CacheManager cacheManager = BeanProvider.getBean(CacheManager.class); + Assert.notNull(cacheManager, "cacheManager is null"); + return cacheManager; + } + + +} diff --git a/src/main/java/com/monkeyk/sos/service/business/CurrentUserJsonDtoLoader.java b/src/main/java/com/monkeyk/sos/service/business/CurrentUserJsonDtoLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..d7c67063efd170ee1b21018fe7d119578745e501 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/service/business/CurrentUserJsonDtoLoader.java @@ -0,0 +1,40 @@ +package com.monkeyk.sos.service.business; + +import com.monkeyk.sos.domain.dto.UserJsonDto; +import com.monkeyk.sos.domain.shared.security.WdcyUserDetails; +import com.monkeyk.sos.domain.user.UserRepository; +import com.monkeyk.sos.web.context.BeanProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.provider.OAuth2Authentication; + +/** + * 2018/10/14 + * + * @author Shengzhao Li + * @since 1.0 + */ +public class CurrentUserJsonDtoLoader { + + + private transient UserRepository userRepository = BeanProvider.getBean(UserRepository.class); + + public CurrentUserJsonDtoLoader() { + } + + public UserJsonDto load() { + + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + final Object principal = authentication.getPrincipal(); + + if (authentication instanceof OAuth2Authentication && + (principal instanceof String || principal instanceof org.springframework.security.core.userdetails.User)) { + OauthUserJsonDtoLoader jsonDtoLoader = new OauthUserJsonDtoLoader((OAuth2Authentication) authentication); + return jsonDtoLoader.load(); + } else { + final WdcyUserDetails userDetails = (WdcyUserDetails) principal; + return new UserJsonDto(userRepository.findByGuid(userDetails.user().guid())); + } + } + +} diff --git a/src/main/java/com/monkeyk/sos/service/business/OauthUserJsonDtoLoader.java b/src/main/java/com/monkeyk/sos/service/business/OauthUserJsonDtoLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..8acf1194aca0edd40c4196986ef54d31582538f9 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/service/business/OauthUserJsonDtoLoader.java @@ -0,0 +1,35 @@ +package com.monkeyk.sos.service.business; + +import com.monkeyk.sos.domain.dto.UserJsonDto; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.provider.OAuth2Authentication; + +import java.util.Collection; + +/** + * 2018/10/14 + * + * @author Shengzhao Li + * @since 1.0 + */ +public class OauthUserJsonDtoLoader { + + private OAuth2Authentication oAuth2Authentication; + + public OauthUserJsonDtoLoader(OAuth2Authentication oAuth2Authentication) { + this.oAuth2Authentication = oAuth2Authentication; + } + + public UserJsonDto load() { + + UserJsonDto userJsonDto = new UserJsonDto(); + userJsonDto.setUsername(oAuth2Authentication.getName()); + + final Collection authorities = oAuth2Authentication.getAuthorities(); + for (GrantedAuthority authority : authorities) { + userJsonDto.getPrivileges().add(authority.getAuthority()); + } + + return userJsonDto; + } +} diff --git a/src/main/java/com/monkeyk/sos/service/business/UserFormSaver.java b/src/main/java/com/monkeyk/sos/service/business/UserFormSaver.java new file mode 100644 index 0000000000000000000000000000000000000000..d185b02b785b217322dc06cc6d576efc3c4a9c9a --- /dev/null +++ b/src/main/java/com/monkeyk/sos/service/business/UserFormSaver.java @@ -0,0 +1,43 @@ +package com.monkeyk.sos.service.business; + +import com.monkeyk.sos.domain.dto.UserFormDto; +import com.monkeyk.sos.domain.user.User; +import com.monkeyk.sos.domain.user.UserRepository; +import com.monkeyk.sos.infrastructure.SOSCacheUtils; +import com.monkeyk.sos.web.WebUtils; +import com.monkeyk.sos.web.context.BeanProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2018/10/14 + * + * @author Shengzhao Li + * @since 1.0 + */ +public class UserFormSaver { + + + private static final Logger LOG = LoggerFactory.getLogger(UserFormSaver.class); + + private transient UserRepository userRepository = BeanProvider.getBean(UserRepository.class); + + + private UserFormDto formDto; + + public UserFormSaver(UserFormDto formDto) { + this.formDto = formDto; + } + + public String save() { + + User user = formDto.newUser(); + userRepository.saveUser(user); + LOG.debug("{}|Save User: {}", WebUtils.getIp(), user); + + //Add to cache + SOSCacheUtils.userCache().put(user.username(), user); + + return user.guid(); + } +} diff --git a/src/main/java/com/monkeyk/sos/service/impl/UserServiceImpl.java b/src/main/java/com/monkeyk/sos/service/impl/UserServiceImpl.java index f7aabe6a210bfbb2f7e13ab50564854783d60ce8..78f0797ba74721827abaecf4c8601e38b258719d 100644 --- a/src/main/java/com/monkeyk/sos/service/impl/UserServiceImpl.java +++ b/src/main/java/com/monkeyk/sos/service/impl/UserServiceImpl.java @@ -8,16 +8,13 @@ import com.monkeyk.sos.domain.shared.security.WdcyUserDetails; import com.monkeyk.sos.domain.user.User; import com.monkeyk.sos.domain.user.UserRepository; import com.monkeyk.sos.service.UserService; +import com.monkeyk.sos.service.business.CurrentUserJsonDtoLoader; +import com.monkeyk.sos.service.business.UserFormSaver; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; -import java.util.Collection; import java.util.List; /** @@ -43,16 +40,8 @@ public class UserServiceImpl implements UserService { @Override public UserJsonDto loadCurrentUserJsonDto() { - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - final Object principal = authentication.getPrincipal(); - - if (authentication instanceof OAuth2Authentication && - (principal instanceof String || principal instanceof org.springframework.security.core.userdetails.User)) { - return loadOauthUserJsonDto((OAuth2Authentication) authentication); - } else { - final WdcyUserDetails userDetails = (WdcyUserDetails) principal; - return new UserJsonDto(userRepository.findByGuid(userDetails.user().guid())); - } + CurrentUserJsonDtoLoader dtoLoader = new CurrentUserJsonDtoLoader(); + return dtoLoader.load(); } @Override @@ -70,21 +59,9 @@ public class UserServiceImpl implements UserService { @Override public String saveUser(UserFormDto formDto) { - User user = formDto.newUser(); - userRepository.saveUser(user); - return user.guid(); + UserFormSaver saver = new UserFormSaver(formDto); + return saver.save(); } - private UserJsonDto loadOauthUserJsonDto(OAuth2Authentication oAuth2Authentication) { - UserJsonDto userJsonDto = new UserJsonDto(); - userJsonDto.setUsername(oAuth2Authentication.getName()); - - final Collection authorities = oAuth2Authentication.getAuthorities(); - for (GrantedAuthority authority : authorities) { - userJsonDto.getPrivileges().add(authority.getAuthority()); - } - - return userJsonDto; - } } \ No newline at end of file diff --git a/src/main/java/com/monkeyk/sos/web/context/BeanProvider.java b/src/main/java/com/monkeyk/sos/web/context/BeanProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..ae3d84527128d6d12c44c74c95d88acd822922b4 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/context/BeanProvider.java @@ -0,0 +1,39 @@ +package com.monkeyk.sos.web.context; + +import org.springframework.context.ApplicationContext; + +/** + * 2018/10/14 + *

      + * Spring bean容器, 启动时初始化 + * + * @author Shengzhao Li + * @see SOSContextLoaderListener + * @since 1.0 + */ +public abstract class BeanProvider { + + private static ApplicationContext springApplicationContext; + + + //private + private BeanProvider() { + } + + static void initialize(ApplicationContext applicationContext) { + BeanProvider.springApplicationContext = applicationContext; + } + + public static T getBean(Class clazz) { + return springApplicationContext == null ? null : springApplicationContext.getBean(clazz); + } + + @SuppressWarnings("unchecked") + public static T getBean(String beanId) { + if (springApplicationContext == null) { + return null; + } + return (T) springApplicationContext.getBean(beanId); + } + +} diff --git a/src/main/java/com/monkeyk/sos/web/context/SOSContextLoaderListener.java b/src/main/java/com/monkeyk/sos/web/context/SOSContextLoaderListener.java new file mode 100644 index 0000000000000000000000000000000000000000..f6122894b1c11008ae8706c5c72147f8880fd8ec --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/context/SOSContextLoaderListener.java @@ -0,0 +1,28 @@ +package com.monkeyk.sos.web.context; + +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.ServletContextEvent; + +/** + * 2018/10/14 + *

      + *

      + * 扩展 Spring Context, 方便获取 bean + * + * @author Shengzhao Li + * @since 1.0 + */ +public class SOSContextLoaderListener extends ContextLoaderListener { + + + @Override + public void contextInitialized(ServletContextEvent event) { + super.contextInitialized(event); + //ext + WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()); + BeanProvider.initialize(applicationContext); + } +} diff --git a/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java b/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java index 7c52f4af634e877de15bbe62eba05ab38d989a31..e893c9b1e2c479be48f4cd754ea093e4f155ba0d 100644 --- a/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java +++ b/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java @@ -137,6 +137,16 @@ public class OAuthRestController implements InitializingBean, ApplicationContext String clientId = getClientId(parameters); ClientDetails authenticatedClient = clientDetailsService.loadClientByClientId(clientId); + //validate client_secret + String clientSecret = getClientSecret(parameters); + if (clientSecret == null || clientSecret.equals("")) { + throw new InvalidClientException("Bad client credentials"); + } else { + if (!clientSecret.equals(authenticatedClient.getClientSecret())) { + throw new InvalidClientException("Bad client credentials"); + } + } + TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(parameters, authenticatedClient); if (clientId != null && !"".equals(clientId)) { @@ -149,9 +159,7 @@ public class OAuthRestController implements InitializingBean, ApplicationContext } } - if (authenticatedClient != null) { - oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); - } + oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); final String grantType = tokenRequest.getGrantType(); if (!StringUtils.hasText(grantType)) { @@ -227,7 +235,7 @@ public class OAuthRestController implements InitializingBean, ApplicationContext * * @param e Exception * @return ResponseEntity - * @throws Exception + * @throws Exception Exception * @see org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint#handleException(Exception) */ @ExceptionHandler(InvalidTokenException.class) @@ -238,18 +246,23 @@ public class OAuthRestController implements InitializingBean, ApplicationContext private boolean isRefreshTokenRequest(Map parameters) { - return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null; + return "refresh_token".equals(parameters.get(OAuth2Utils.GRANT_TYPE)) && parameters.get("refresh_token") != null; } private boolean isAuthCodeRequest(Map parameters) { - return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null; + return "authorization_code".equals(parameters.get(OAuth2Utils.GRANT_TYPE)) && parameters.get("code") != null; } protected String getClientId(Map parameters) { - return parameters.get("client_id"); + return parameters.get(OAuth2Utils.CLIENT_ID); + } + + protected String getClientSecret(Map parameters) { + return parameters.get("client_secret"); } + private AuthenticationManager getAuthenticationManager() { return this.authenticationManager; } diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 220efb57cfb9db4c436bfafb2d16a03426f66239..81351d794435f8256ab7ba8c885e38dc8a864da8 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -132,11 +132,11 @@ - org.springframework.web.context.ContextLoaderListener + com.monkeyk.sos.web.context.SOSContextLoaderListener diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 41ba3456ea13190e68e2372c092b52fd0bc2ca4e..a2ddf8662f5ffbabd01abfb30c51fde7c492b1da 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -5,6 +5,7 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" trimDirectiveWhitespaces="true" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> @@ -17,11 +18,20 @@ 1.0 -

      - Login -  |  - Logout -

      + + Logged: +

      + Logout +

      +
      + +

      + Login +  |  + Logout +

      +
      +
      操作说明: