Browse Source

first commit

wangweiqun 6 years ago
commit
1060074c8b
40 changed files with 7075 additions and 0 deletions
  1. 1159 0
      ehr-cloud/pom.xml
  2. 606 0
      ehr-ms-parent-pom/pom.xml
  3. 148 0
      pom.xml
  4. 50 0
      readme.md
  5. 37 0
      src/main/java/com/yihu/ehr/AuthenticationServer.java
  6. 34 0
      src/main/java/com/yihu/ehr/oauth2/client/FzApiClient.java
  7. 39 0
      src/main/java/com/yihu/ehr/oauth2/config/DataSourceConfig.java
  8. 239 0
      src/main/java/com/yihu/ehr/oauth2/config/OAuth2Config.java
  9. 117 0
      src/main/java/com/yihu/ehr/oauth2/config/SecurityConfig.java
  10. 75 0
      src/main/java/com/yihu/ehr/oauth2/interceptor/RateLimitInterceptor.java
  11. 42 0
      src/main/java/com/yihu/ehr/oauth2/interceptor/RateLimitService.java
  12. 33 0
      src/main/java/com/yihu/ehr/oauth2/interceptor/UserAgentInterceptor.java
  13. 63 0
      src/main/java/com/yihu/ehr/oauth2/model/AccessToken.java
  14. 250 0
      src/main/java/com/yihu/ehr/oauth2/model/EhrUserDetails.java
  15. 38 0
      src/main/java/com/yihu/ehr/oauth2/model/Oauth2Failed.java
  16. 45 0
      src/main/java/com/yihu/ehr/oauth2/model/VerifyCode.java
  17. 76 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrOAuth2AuthenticationEntryPoint.java
  18. 153 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrOAuth2ExceptionTranslator.java
  19. 90 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenExtractor.java
  20. 355 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenGranter.java
  21. 438 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenServices.java
  22. 68 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/EhrUserDetailsService.java
  23. 57 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryAuthorizationCodeService.java
  24. 53 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryClientDetailsService.java
  25. 297 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryTokenStoreService.java
  26. 55 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcAccessUrlService.java
  27. 82 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcAuthorizationCodeService.java
  28. 43 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcClientDetailsService.java
  29. 459 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcTokenStore.java
  30. 49 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcUserSecurityService.java
  31. 80 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisApiAccessValidator.java
  32. 395 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisTokenStore.java
  33. 46 0
      src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisVerifyCodeService.java
  34. 21 0
      src/main/java/com/yihu/ehr/oauth2/utils/BasicAuthorizationExtractor.java
  35. 275 0
      src/main/java/com/yihu/ehr/oauth2/web/EhrAccessTokenEndpoint.java
  36. 339 0
      src/main/java/com/yihu/ehr/oauth2/web/EhrAuthLoginEndpoint.java
  37. 580 0
      src/main/java/com/yihu/ehr/oauth2/web/EhrAuthorizationEndpoint.java
  38. 37 0
      src/main/resources/application.yml
  39. 29 0
      src/main/resources/banner.txt
  40. 23 0
      src/main/resources/bootstrap.yml

+ 1159 - 0
ehr-cloud/pom.xml

@ -0,0 +1,1159 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.yihu.ehr</groupId>
    <artifactId>ehr-cloud</artifactId>
    <version>1.2.0</version>
    <packaging>pom</packaging>
    <name>ehr-cloud</name>
    <description>EHR parent pom for all</description>
    <url>http://ehr.yihu.com</url>
    <organization>
        <name>JKZL Software, Inc.</name>
        <url>http://www.yihu.com</url>
    </organization>
    <licenses>
        <license>
            <name>Apache License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0</url>
        </license>
    </licenses>
    <developers>
        <developer>
            <id>sand</id>
            <name>Sand Wen</name>
            <email>sand.fj.wen@gmail.com</email>
            <organization>JKZL Software, Inc.</organization>
            <organizationUrl>http://www.yihu.com</organizationUrl>
            <roles>
                <role>Project lead</role>
                <role>Project designer</role>
                <role>Project programmer</role>
            </roles>
        </developer>
        <developer>
            <id>yzh</id>
            <name>叶泽华</name>
            <email>yzh@qq.com</email>
            <organization>JKZL Software, Inc.</organization>
            <organizationUrl>http://www.yihu.com</organizationUrl>
            <roles>
                <role>Project programmer</role>
            </roles>
        </developer>
        <developer>
            <id>cws</id>
            <name>陈维山</name>
            <email>hill9868@qq.com</email>
            <organization>JKZL Software, Inc.</organization>
            <organizationUrl>http://www.yihu.com</organizationUrl>
            <roles>
                <role>Project programmer</role>
            </roles>
        </developer>
        <developer>
            <id>hzy</id>
            <name>黄志勇</name>
            <email>hzy@qq.com</email>
            <organization>JKZL Software, Inc.</organization>
            <organizationUrl>http://www.yihu.com</organizationUrl>
            <roles>
                <role>Project programmer</role>
            </roles>
        </developer>
    </developers>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <version.spring-framework>4.3.13.RELEASE</version.spring-framework>
        <version.spring-boot>1.5.9.RELEASE</version.spring-boot>
        <version.spring-cloud-starter>1.3.2.RELEASE</version.spring-cloud-starter>
        <version.spring-cloud>1.4.2.RELEASE</version.spring-cloud>
        <version.spring-security>4.2.3.RELEASE</version.spring-security>
        <version.spring-oauth2>2.0.14.RELEASE</version.spring-oauth2>
        <version.spring-session>1.3.1.RELEASE</version.spring-session>
        <version.spring-session-hazelcast>2.0.1.RELEASE</version.spring-session-hazelcast>
        <version.spring-data-commons>1.13.9.RELEASE</version.spring-data-commons>
        <version.spring-data-hadoop>2.2.0.RELEASE</version.spring-data-hadoop>
        <version.spring-data-solr>2.1.3.RELEASE</version.spring-data-solr>
        <version.spring-data-redis>1.7.1.RELEASE</version.spring-data-redis>
        <version.spring-data-jpa>1.11.10.RELEASE</version.spring-data-jpa>
        <version.spring-kafka>1.0.5.RELEASE</version.spring-kafka>
        <version.commons-bean-utils>1.9.2</version.commons-bean-utils>
        <version.commons-codec>1.9</version.commons-codec>
        <version.commons-collections>3.2.1</version.commons-collections>
        <version.commons-compress>1.9</version.commons-compress>
        <version.commons-dbcp2>2.1.1</version.commons-dbcp2>
        <version.commons-dbutils>1.6</version.commons-dbutils>
        <version.commons-io>2.4</version.commons-io>
        <version.commons-lang3>3.2.1</version.commons-lang3>
        <version.commons-pool2>2.4.2</version.commons-pool2>
        <version.zookeeper>3.4.6</version.zookeeper>
        <version.hadoop-client>2.6.5</version.hadoop-client>
        <version.hbase-client>1.1.1</version.hbase-client>
        <version.solr>5.5.4</version.solr>
        <version.hibernate>4.3.11.Final</version.hibernate>
        <version.hibernate-validator>6.0.10.Final</version.hibernate-validator>
        <version.hibernate-jpa-api>1.0.0.Final</version.hibernate-jpa-api>
        <version.http-core>4.4.3</version.http-core>
        <version.http-client>4.5.1</version.http-client>
        <version.http-mime>4.5.1</version.http-mime>
        <version.io-dropwizard-metrics>3.1.2</version.io-dropwizard-metrics>
        <version.java>1.8</version.java>
        <version.jackson>2.6.6</version.jackson>
        <version.jedis>2.9.0</version.jedis>
        <version.jcl-over-slf4j>1.7.19</version.jcl-over-slf4j>
        <version.jul-over-slf4j>1.7.21</version.jul-over-slf4j>
        <version.joda-time>2.8.2</version.joda-time>
        <version.junit>4.12</version.junit>
        <version.logging>1.2</version.logging>
        <version.log4j>1.2.17</version.log4j>
        <version.log4j2>2.4.1</version.log4j2>
        <version.logback>1.1.7</version.logback>
        <version.mysql>5.1.45</version.mysql>
        <version.pinyin4j>2.5.0</version.pinyin4j>
        <version.quartz>2.2.3</version.quartz>
        <version.servlet-api>3.1.0</version.servlet-api>
        <version.slf4j>1.7.21</version.slf4j>
        <version.statsd-client>3.1.0</version.statsd-client>
        <version.swagger>2.7.0</version.swagger>
        <version.swagger-ui>2.7.0</version.swagger-ui>
        <version.thrift>0.9.1</version.thrift>
        <version.tomcat-embed>8.5.27</version.tomcat-embed>
        <version.websocket-api>1.1</version.websocket-api>
        <version.zip4j>1.3.2</version.zip4j>
        <version.poi>3.12</version.poi>
        <version.scala>2.10.6</version.scala>
        <version.elasticsearch>2.1.0</version.elasticsearch>
        <version.elasticsearch-sql>2.4.1.0</version.elasticsearch-sql>
        <version.jest>2.4.0</version.jest>
        <version.alibaba-druid>1.0.15</version.alibaba-druid>
        <version.feign>9.5.0</version.feign>
        <version.hystrix>1.5.10</version.hystrix>
        <version.archaius>0.7.5</version.archaius>
        <version.ehr>1.2.0</version.ehr>
        <version.eip>1.3.1</version.eip>
        <version.json>20160212</version.json>
        <version.json-lib>2.4</version.json-lib>
        <version.fastjson>1.2.17</version.fastjson>
        <version.commons-net>3.3</version.commons-net>
        <version.jxl>2.6</version.jxl>
        <version.fastdfs>1.27</version.fastdfs>
        <version.spring.boot.admin>1.5.7</version.spring.boot.admin>
        <version.jettison>1.3.7</version.jettison>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--<dependency>-->
                <!--<groupId>org.springframework.boot</groupId>-->
                <!--<artifactId>spring-boot-dependencies</artifactId>-->
                <!--<version>1.5.9.RELEASE</version>-->
                <!--<type>pom</type>-->
                <!--<scope>import</scope>-->
            <!--</dependency>-->
            <!--<dependency>-->
                <!--<groupId>org.springframework.cloud</groupId>-->
                <!--<artifactId>spring-cloud-dependencies</artifactId>-->
                <!--<version>Finchley.M5</version>-->
                <!--<type>pom</type>-->
                <!--<scope>import</scope>-->
            <!--</dependency>-->
            <!-- Base library-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>${version.servlet-api}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${version.junit}</version>
                <scope>test</scope>
            </dependency>
            <!-- Spring framework family -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-expression</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-messaging</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-oxm</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${version.spring-framework}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
            <dependency>
                <groupId>org.springframework.kafka</groupId>
                <artifactId>spring-kafka</artifactId>
                <version>${version.spring-kafka}</version>
            </dependency>
            <!-- Spring boot family -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-actuator</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-batch</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${version.spring-boot}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-mongodb</artifactId>
                <version>${version.spring-boot}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Spring cloud family -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter</artifactId>
                <version>${version.spring-cloud-starter}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-config-server</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-archaius</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka-server</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-netflix-eureka</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-feign</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-ribbon</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zuul</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-hystrix</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
                <version>${version.spring-cloud}</version>
            </dependency>
            <!-- Feign -->
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-core</artifactId>
                <version>${version.feign}</version>
            </dependency>
            <!-- Hystrix -->
            <dependency>
                <groupId>com.netflix.hystrix</groupId>
                <artifactId>hystrix-core</artifactId>
                <version>${version.hystrix}</version>
            </dependency>
            <!-- Archaius -->
            <dependency>
                <groupId>com.netflix.archaius</groupId>
                <artifactId>archaius-core</artifactId>
                <version>${version.archaius}</version>
            </dependency>
            <!-- Spring data family -->
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-commons</artifactId>
                <version>${version.spring-data-commons}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-hadoop-hbase</artifactId>
                <version>${version.spring-data-hadoop}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-solr</artifactId>
                <version>${version.spring-data-solr}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Spring session family-->
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-hazelcast</artifactId>
                <version>${version.spring-session-hazelcast}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session</artifactId>
                <version>${version.spring-session}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
                <version>${version.spring-session}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Spring Security -->
            <!--<dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${version.spring-security}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-core</artifactId>
                <version>${version.spring-security}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-crypto</artifactId>
                <version>${version.spring-security}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${version.spring-security}</version>
                <scope>${dependency.scope}</scope>
            </dependency>-->
            <!-- Oauth2 -->
            <dependency>
                <groupId>org.springframework.security.oauth</groupId>
                <artifactId>spring-security-oauth2</artifactId>
                <version>${version.spring-oauth2}</version>
                <scope>${dependency.scope}</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>*</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!--Jackson library -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>${version.jackson}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>${version.jackson}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>${version.jackson}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Http library -->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore</artifactId>
                <version>${version.http-core}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${version.http-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpmime</artifactId>
                <version>${version.http-mime}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!--Apache commons library -->
            <dependency>
                <groupId>org.codehaus.woodstox</groupId>
                <artifactId>stax2-api</artifactId>
                <version>3.1.4</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.codehaus.woodstox</groupId>
                <artifactId>woodstox-core-asl</artifactId>
                <version>4.4.1</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.noggit</groupId>
                <artifactId>noggit</artifactId>
                <version>0.6</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${version.commons-lang3}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>${version.poi}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
                <version>${version.commons-bean-utils}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
                <version>${version.commons-pool2}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>commons-dbutils</groupId>
                <artifactId>commons-dbutils</artifactId>
                <version>${version.commons-dbutils}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-dbcp2</artifactId>
                <version>${version.commons-dbcp2}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${version.commons-io}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>${version.commons-collections}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${version.commons-codec}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-compress</artifactId>
                <version>${version.commons-compress}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Mysql library -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${version.mysql}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Hibernate framework library -->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-core</artifactId>
                <version>${version.hibernate}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>${version.hibernate-validator}</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-entitymanager</artifactId>
                <version>${version.hibernate}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.hibernate.javax.persistence</groupId>
                <artifactId>hibernate-jpa-2.1-api</artifactId>
                <version>${version.hibernate-jpa-api}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Zookeeper library -->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>${version.zookeeper}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Hadoop library -->
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-annotations</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-auth</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-common</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-distcp</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-hdfs</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-mapreduce-client-common</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-mapreduce-client-core</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-mapreduce-client-shuffle</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-streaming</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-yarn-api</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-yarn-client</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-yarn-common</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-yarn-server-common</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-yarn-server-nodemanager</artifactId>
                <version>${version.hadoop-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.hbase</groupId>
                <artifactId>hbase-client</artifactId>
                <version>${version.hbase-client}</version>
                <scope>${dependency.scope}</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.hadoop</groupId>
                        <artifactId>*</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.apache.hbase</groupId>
                <artifactId>hbase-common</artifactId>
                <version>${version.hbase-client}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.apache.hbase</groupId>
                <artifactId>hbase-protocol</artifactId>
                <version>${version.hbase-client}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- export library -->
            <dependency>
                <groupId>net.sourceforge.jexcelapi</groupId>
                <artifactId>jxl</artifactId>
                <version>${version.jxl}</version>
            </dependency>
            <!-- Google library -->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>18.0</version>
            </dependency>
            <dependency>
                <groupId>com.google.code.findbugs</groupId>
                <artifactId>jsr305</artifactId>
                <version>3.0.1</version>
            </dependency>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.6.2</version>
            </dependency>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>2.5.0</version>
            </dependency>
            <dependency>
                <groupId>com.google.inject</groupId>
                <artifactId>guice</artifactId>
                <version>4.1.0</version>
            </dependency>
            <!-- Solr library -->
            <dependency>
                <groupId>org.apache.solr</groupId>
                <artifactId>solr-core</artifactId>
                <version>${version.solr}</version>
                <scope>${dependency.scope}</scope>
                <exclusions>
                    <exclusion>
                        <groupId>commons-lang</groupId>
                        <artifactId>commons-lang</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.apache.solr</groupId>
                <artifactId>solr-solrj</artifactId>
                <version>${version.solr}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-lang</groupId>
                        <artifactId>commons-lang</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- Elasticsearch -->
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>${version.elasticsearch}</version>
            </dependency>
            <dependency>
                <groupId>org.nlpcn</groupId>
                <artifactId>elasticsearch-sql</artifactId>
                <version>${version.elasticsearch-sql}</version>
            </dependency>
            <!-- Jest -->
            <dependency>
                <groupId>io.searchbox</groupId>
                <artifactId>jest</artifactId>
                <version>${version.jest}</version>
            </dependency>
            <!-- Redis library -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>${version.jedis}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- FastDFS library -->
            <dependency>
                <groupId>org.csource</groupId>
                <artifactId>fastdfs-client-java</artifactId>
                <version>${version.fastdfs}</version>
            </dependency>
            <!-- Quartz library -->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>${version.quartz}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>${version.quartz}</version>
            </dependency>
            <!-- Zip library -->
            <dependency>
                <groupId>net.lingala.zip4j</groupId>
                <artifactId>zip4j</artifactId>
                <version>${version.zip4j}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Pinyin library -->
            <dependency>
                <groupId>com.belerweb</groupId>
                <artifactId>pinyin4j</artifactId>
                <version>${version.pinyin4j}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Swagger-ui library -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${version.swagger}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${version.swagger-ui}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- date Time util library -->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>${version.joda-time}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.thrift</groupId>
                <artifactId>libthrift</artifactId>
                <version>${version.thrift}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>com.timgroup</groupId>
                <artifactId>java-statsd-client</artifactId>
                <version>${version.statsd-client}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>io.dropwizard.metrics</groupId>
                <artifactId>metrics-core</artifactId>
                <version>${version.io-dropwizard-metrics}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Log framework library -->
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>${version.logging}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${version.log4j}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${version.slf4j}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${version.logback}</version>
                <scope>${dependency.scope}</scope>
            </dependency>
            <!-- Alibaba -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${version.alibaba-druid}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${version.fastjson}</version>
            </dependency>
            <!-- Spring Boot Admin -->
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-starter-server</artifactId>
                <version>${version.spring.boot.admin}</version>
            </dependency>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-server-ui</artifactId>
                <version>${version.spring.boot.admin}</version>
            </dependency>
            <!-- Extend library-->
            <dependency>
                <groupId>org.codehaus.jettison</groupId>
                <artifactId>jettison</artifactId>
                <version>${version.jettison}</version>
            </dependency>
            <dependency>
                <groupId>org.json</groupId>
                <artifactId>json</artifactId>
                <version>${version.json}</version>
            </dependency>
            <dependency>
                <groupId>net.sf.json-lib</groupId>
                <artifactId>json-lib</artifactId>
                <version>${version.json-lib}</version>
            </dependency>
            <dependency>
                <groupId>commons-net</groupId>
                <artifactId>commons-net</artifactId>
                <version>${version.commons-net}</version>
            </dependency>
            <!-- 个推相关 -->
            <dependency>
                <groupId>com.gexin.platform</groupId>
                <artifactId>gexin-rp-sdk-http</artifactId>
                <version>4.0.1.17</version>
            </dependency>
            <dependency>
                <groupId>com.gexin.platform</groupId>
                <artifactId>gexin-rp-fastjson</artifactId>
                <version>1.0.0.1</version>
            </dependency>
            <dependency>
                <groupId>com.gexin.platform</groupId>
                <artifactId>gexin-rp-sdk-base</artifactId>
                <version>4.0.0.22</version>
            </dependency>
            <dependency>
                <groupId>com.gexin.platform</groupId>
                <artifactId>gexin-rp-sdk-template</artifactId>
                <version>4.0.0.16</version>
            </dependency>
            <dependency>
                <groupId>com.gexin.platform</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>2.5.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <repositories>
        <repository>
            <id>public</id>
            <name>public</name>
            <url>http://172.19.103.43:8081/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>public</name>
            <url>http://172.19.103.43:8081/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-ssh</artifactId>
                <version>2.10</version>
            </extension>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-http-lightweight</artifactId>
                <version>2.10</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <verbose/>
                        <bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
                    </compilerArguments>
                </configuration>
                <version>3.1</version>
            </plugin>
        </plugins>
    </build>
</project>

+ 606 - 0
ehr-ms-parent-pom/pom.xml

@ -0,0 +1,606 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.yihu.ehr</groupId>
        <artifactId>ehr-cloud</artifactId>
        <version>1.2.0</version>
        <relativePath>../ehr-cloud/pom.xml</relativePath>
    </parent>
    <artifactId>ehr-ms-parent-pom</artifactId>
    <packaging>pom</packaging>
    <description>EHR micro service parent pom</description>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.jooq</groupId>
                    <artifactId>jooq-codegen-maven</artifactId>
                    <version>${jooq.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring-boot.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.7</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.5.1</version>
                    <configuration>
                        <recompressZippedFiles>false</recompressZippedFiles>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>2.10</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-eclipse-plugin</artifactId>
                    <version>2.9</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <version>2.18</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-help-plugin</artifactId>
                    <version>2.2</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                    <version>2.10.1</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>2.7</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.3</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.apache.maven.doxia</groupId>
                            <artifactId>doxia-module-markdown</artifactId>
                            <version>1.5</version>
                        </dependency>
                    </dependencies>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>2.4</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.18.1</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>1.9.1</version>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.3.2</version>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>versions-maven-plugin</artifactId>
                    <version>2.2</version>
                </plugin>
                <plugin>
                    <groupId>pl.project13.maven</groupId>
                    <artifactId>git-commit-id-plugin</artifactId>
                    <version>2.1.11</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${version.spring-boot}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
                <version>2.17</version>
                <executions>
                    <execution>
                        <id>checkstyle-validation</id>
                        <phase>validate</phase>
                        <configuration>
                            <skip>true</skip>
                            <configLocation>src/checkstyle/checkstyle.xml</configLocation>
                            <suppressionsLocation>src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
                            <headerLocation>src/checkstyle/checkstyle-header.txt</headerLocation>
                            <propertyExpansion>checkstyle.build.directory=${project.build.directory}</propertyExpansion>
                            <encoding>UTF-8</encoding>
                            <consoleOutput>true</consoleOutput>
                            <failsOnError>true</failsOnError>
                            <includeTestSourceDirectory>true</includeTestSourceDirectory>
                        </configuration>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${version.java}</source>
                    <target>${version.java}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <id>enforce-rules</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <requireJavaVersion>
                                    <version>[1.8,)</version>
                                </requireJavaVersion>
                                <!--<requireProperty>
                                    <property>main.basedir</property>
                                </requireProperty>-->
                                <!--<requireProperty>
                                    <property>project.organization.name</property>
                                </requireProperty>-->
                                <!--<requireProperty>
                                    <property>project.name</property>
                                </requireProperty>-->
                                <!--<requireProperty>
                                    <property>project.description</property>
                                </requireProperty>-->
                            </rules>
                            <fail>true</fail>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <includes>
                        <include>**/*Tests.java</include>
                    </includes>
                    <excludes>
                        <exclude>**/Abstract*.java</exclude>
                    </excludes>
                    <!--<systemPropertyVariables>-->
                    <!--<java.security.egd>file:/dev/./urandom</java.security.egd>-->
                    <!--<java.awt.headless>true</java.awt.headless>-->
                    <!--</systemPropertyVariables>-->
                    <argLine>-Xmx1024m</argLine>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!--<plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>animal-sniffer-maven-plugin</artifactId>
                <configuration>
                    <skip>${disable.checks}</skip>
                    <signature>
                        <groupId>org.codehaus.mojo.signature</groupId>
                        <artifactId>java16</artifactId>
                        <version>1.0</version>
                    </signature>
                    <annotations>
                        <annotation>org.springframework.lang.UsesJava8</annotation>
                        <annotation>org.springframework.lang.UsesJava7</annotation>
                        <annotation>org.springframework.boot.lang.UsesUnsafeJava</annotation>
                    </annotations>
                </configuration>
                <executions>
                    <execution>
                        <id>enforce-java-6</id>
                        <phase>test</phase>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>-->
        </plugins>
    </build>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-admin-gateway-model</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-elasticsearch</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-fastdfs</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-hbase</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-mysql</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-query</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-redis</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-data-solr</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-ehr-constants</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-entity</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <!--<dependency>-->
            <!--<groupId>com.yihu.ehr</groupId>-->
            <!--<artifactId>commons-metrics</artifactId>-->
            <!--<version>${version.ehr}</version>-->
            <!--</dependency>-->
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-profile-core</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-redis-mq</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-rest-model</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.hos</groupId>
                <artifactId>common-rest-model</artifactId>
                <version>${version.eip}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-ui-swagger</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-util</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-web</artifactId>
                <version>${version.ehr}</version>
            </dependency>
            <!--<dependency>
                <groupId>com.yihu.ehr</groupId>
                <artifactId>commons-metrics</artifactId>
                <version>${version.ehr}</version>
            </dependency>-->
        </dependencies>
    </dependencyManagement>
    <!--
    profiles分为三种场景(dev,test,prod),三种部署模式(jar,war,docker).预计是9种模式,
    但目前仅使用到dev,test,prod的jar,test的war.若有需要可以组合配置这些部署模式.
    - dev的可执行jar包,在本机调试,不需要配置wagon参数。
    - test,prod的可执行jar包,需要在编译后传送到服务器上部署,故需配置wagon参数,参数可根据服务需要自行配置。
    - dev,test,prod的war包,编译后使用tomcat api部署,故需配置tomcat参数,参数可根据服务需要自行配置。
    -->
    <profiles>
        <profile>
            <id>dev-jar</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <skipTests>true</skipTests>
                <packaging.type>jar</packaging.type>
                <dependency.scope>compile</dependency.scope>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </dependency>
                <!--<dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>-->
            </dependencies>
        </profile>
        <profile>
            <id>test-jar</id>
            <properties>
                <skipTests>true</skipTests>
                <packaging.type>jar</packaging.type>
                <dependency.scope>compile</dependency.scope>
                <wagonServerId/>
                <wagonUrl/>
            </properties>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>wagon-maven-plugin</artifactId>
                        <version>1.0</version>
                        <configuration>
                            <serverId>${wagonServerId}</serverId>
                            <fromFile>${project.build.directory}/${project.build.finalName}.jar</fromFile>
                            <url>${wagonUrl}</url>
                            <commands>
                                <command>pkill -f ${project.build.finalName}.jar</command>
                                <command>nohub java -Djava.security.egd=file:/dev/./urandom -jar
                                    ${project.build.finalName}.jar &amp;
                                </command>
                            </commands>
                            <displayCommandOutputs>true</displayCommandOutputs>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-logging</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>test-war</id>
            <activation>
                <property>
                    <name>spring.profiles.active</name>
                    <value>default,test</value>
                </property>
            </activation>
            <properties>
                <skipTests>true</skipTests>
                <packaging.type>war</packaging.type>
                <dependency.scope>provided</dependency.scope>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <scope>${dependency.scope}</scope>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-logging</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>
            </dependencies>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.tomcat.maven</groupId>
                        <artifactId>tomcat7-maven-plugin</artifactId>
                        <version>2.2</version>
                        <configuration>
                            <url>http://localhost:8080/manager/text</url>
                            <server>tomcat8</server>
                            <username>deployer</username>
                            <password>jkzldeployer</password>
                            <path>/${project.artifactId}</path>
                            <update>true</update>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>prod-jar</id>
            <activation>
                <property>
                    <name>spring.profiles.active</name>
                    <value>default,prod</value>
                </property>
            </activation>
            <properties>
                <skipTests>true</skipTests>
                <packaging.type>war</packaging.type>
                <dependency.scope>compile</dependency.scope>
                <wagonServerId>11.1.2.21</wagonServerId>
                <wagonUrl>scp://user:password@11.1.2.21/home/root/ehr-release</wagonUrl>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <scope>${dependency.scope}</scope>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-logging</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>prod-war</id>
            <properties>
                <skipTests>true</skipTests>
                <packaging.type>war</packaging.type>
                <dependency.scope>provided</dependency.scope>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <scope>${dependency.scope}</scope>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-logging</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <scope>${dependency.scope}</scope>
                </dependency>
            </dependencies>
            <build>
                <plugins>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

+ 148 - 0
pom.xml

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.yihu.ehr</groupId>
        <artifactId>ehr-ms-parent-pom</artifactId>
        <version>1.2.0</version>
        <relativePath>ehr-ms-parent-pom/pom.xml</relativePath>
    </parent>
    <artifactId>svr-authentication</artifactId>
    <properties>
        <wagonServerId>192.168.1.220</wagonServerId>
        <wagonUrl>scp://sand:timeneverstop@192.168.1.221/home/sand/ehr-release</wagonUrl>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Redis  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <!-- Redis  -->
        <!-- JdbcTemplate-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
        </dependency>
        <!-- JdbcTemplate-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        <dependency>
            <groupId>com.yihu.ehr</groupId>
            <artifactId>commons-ui-swagger</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.yihu.ehr</groupId>
            <artifactId>commons-ehr-constants</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.yihu.ehr</groupId>
            <artifactId>commons-rest-model</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>2.7</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.3</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.doxia</groupId>
                        <artifactId>doxia-module-markdown</artifactId>
                        <version>1.5</version>
                    </dependency>
                    <dependency>
                        <groupId>lt.velykis.maven.skins</groupId>
                        <artifactId>reflow-velocity-tools</artifactId>
                        <version>1.1.1</version>
                    </dependency>
                    <dependency>
                        <groupId>org.apache.velocity</groupId>
                        <artifactId>velocity</artifactId>
                        <version>1.7</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <locales>zh_CN</locales>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

+ 50 - 0
readme.md

@ -0,0 +1,50 @@
授权码模式 authorization_code
1.GET http://localhost:10260/oauth/authorize?response_type=code&client_id=uzs5G0HgTp&state=sxy&scope=read&redirect_uri=http://192.168.1.221:8010/ehr/browser/common/login/signin&pk=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjgKCe2zFmYSjHZz93cO17A0Hj6H5XdfwP2I5Qu1UJbmhGG/wPu409r9TABXblcxC9uPVR3PJ5dPrWPLuQ/r7tq16vSa5GF4iCcSlLyx/IDA5bq8ZnafS/RjPiKDtdZAx5uCNLog9GkVHNZIhK9cS9MI4QNuOJzOXaAVwnP2wIDAQAB
    HTTP/1.1 302 Found
    Location: http://localhost:9000/info?code=Ar0mp5&state=sxy
2.POST  http://localhost:10260/oauth/accessToken
    grant_type=authorization_code&client_id=uzs5G0HgTp&code=Ar0mp5&redirect_uri=http://192.168.1.221:8010/ehr/browser/common/login/signin
    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    {
      "accessToken":"4c5c7f4f-158a-4f73-9c14-ecbe78671872",
      "tokenType":"bearer",
      "expiresIn":3600,
      "refreshToken":"4c5c7f4f-158a-4f73-9c14-ecbe78671873",
      "state":"example_value"
    }
简化模式 implicit
1.GET http://localhost:10260/oauth/authorize?response_type=token&client_id=uzs5G0HgTp&state=sxy&scope=read&redirect_uri=http://192.168.1.221:8010/ehr/browser/common/login/signin&pk=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjgKCe2zFmYSjHZz93cO17A0Hj6H5XdfwP2I5Qu1UJbmhGG/wPu409r9TABXblcxC9uPVR3PJ5dPrWPLuQ/r7tq16vSa5GF4iCcSlLyx/IDA5bq8ZnafS/RjPiKDtdZAx5uCNLog9GkVHNZIhK9cS9MI4QNuOJzOXaAVwnP2wIDAQAB
    HTTP/1.1 302 Found
    Location: https://www.baidu.com/#access_token=4c5c7f4f-158a-4f73-9c14-ecbe78671872&token_type=bearer&state=sxy&expires_in=2418755
   
密码模式 password
1.POST http://localhost:10260/oauth/accessToken
    grant_type=password&username=admin&password=123456789&scope=read&client_id=uzs5G0HgTp
    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    {
      "accessToken":"8ce77011-87c9-48f3-8e4f-5cf945f047c7",
      "tokenType":"example",
      "expiresIn":3600,
      "refreshToken":"6b010e33-8bc3-4f3f-8123-66df589dc82f",
      "state":"example_value",
      "user":"admin"
     }
更新令牌 refresh_token
1.POST http://localhost:10260/oauth/accessToken
    grant_type=refresh_token&refresh_token=6b010e33-8bc3-4f3f-8123-66df589dc82f&client_id=uzs5G0HgTp
单点登陆 sso
1.GET http://172.19.103.73:10260/oauth/sso?response_type=token&client_id=uzs5G0HgTp&state=sxy&scope=read&redirect_uri=http://192.168.1.221:8010/ehr/browser/common/login/signin?idCardNo=362321200108017313&user=admin
2.GET http://localhost:10260/oauth/sso?response_type=token&client_id=uzs5G0HgTp&state=sxy&scope=read&redirect_uri=http://localhost:8010/ehr/browser/common/login/signin?idCardNo=362321200108017313&user=admin
3.GET http://172.19.103.73:10260/oauth/sso?response_type=token&client_id=uzs5G0HgTp&state=sxy&scope=read&redirect_uri=http://192.168.131.150:8010/ehr/browser/common/login/signin?idCardNo=362321200108017313&user=admin

+ 37 - 0
src/main/java/com/yihu/ehr/AuthenticationServer.java

@ -0,0 +1,37 @@
package com.yihu.ehr;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.net.SocketException;
@Configuration
@EnableAutoConfiguration(exclude = {
		DataSourceAutoConfiguration.class,
		HibernateJpaAutoConfiguration.class,
		DataSourceTransactionManagerAutoConfiguration.class})
@ComponentScan
@EnableDiscoveryClient
@EnableFeignClients
@EnableEurekaClient
public class AuthenticationServer extends SpringBootServletInitializer {
    public static void main(String[] args) throws SocketException {
		SpringApplication.run(AuthenticationServer.class, args);
	}
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(AuthenticationServer.class);
    }
}

+ 34 - 0
src/main/java/com/yihu/ehr/oauth2/client/FzApiClient.java

@ -0,0 +1,34 @@
package com.yihu.ehr.oauth2.client;
import com.yihu.ehr.constants.ApiVersion;
import com.yihu.ehr.constants.MicroServices;
import com.yihu.ehr.constants.ServiceApi;
import com.yihu.ehr.util.rest.Envelop;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import springfox.documentation.annotations.ApiIgnore;
/**
 * @author 张进军
 * @date 2018/7/4 10:52
 */
@FeignClient(name = MicroServices.Basic)
@RequestMapping(value = ApiVersion.Version1_0)
@ApiIgnore
public interface FzApiClient {
    @ApiOperation("转发福州总部内网接口")
    @RequestMapping(value = ServiceApi.Fz.InnerApi, method = RequestMethod.POST)
    Envelop fzInnerApi(
            @ApiParam(name = "api", value = "API 名称,格式为 a.b.c", required = true)
            @RequestParam(value = "api") String api,
            @ApiParam(name = "paramsJson", value = "参数JSON字符串", required = true)
            @RequestParam(value = "paramsJson") String paramsJson,
            @ApiParam(name = "apiVersion", value = "API 版本号,版本号为整型,从数字 1 开始递增", required = true)
            @RequestParam(value = "apiVersion") int apiVersion);
}

+ 39 - 0
src/main/java/com/yihu/ehr/oauth2/config/DataSourceConfig.java

@ -0,0 +1,39 @@
package com.yihu.ehr.oauth2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/**
 * Created by Administrator on 2017/2/21.
 */
@Configuration
public class DataSourceConfig {
    /**
     * 主数据源
     *
     * @return
     */
    @Bean
    @Primary//主库 默认不写名字用这个
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryReadWriteDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }
}

+ 239 - 0
src/main/java/com/yihu/ehr/oauth2/config/OAuth2Config.java

@ -0,0 +1,239 @@
package com.yihu.ehr.oauth2.config;
import com.yihu.ehr.oauth2.oauth2.*;
import com.yihu.ehr.oauth2.oauth2.jdbc.*;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisTokenStore;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisVerifyCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import javax.sql.DataSource;
import java.io.Serializable;
/**
 * @author Sand
 * @version 1.0
 * @created 2016.03.03 20:50
 */
@Configuration
public class OAuth2Config {
    private static final String RESOURCE_ID = "ehr";
    @Autowired
    private DataSource dataSource;
    @Configuration
    @EnableAuthorizationServer
    public static class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
        @Autowired
        private AuthenticationManager authenticationManager;
        @Autowired
        private AuthorizationCodeServices inMemoryAuthorizationCodeServices;
        @Autowired
        private AuthorizationServerTokenServices ehrTokenServices;
        @Autowired
        private ClientDetailsService ehrJdbcClientDetailsService;
        @Autowired
        private TokenGranter ehrTokenGranter;
        /*@Autowired
        private TokenStore ehrJdbcTokenStore;*/
        @Autowired
        private TokenStore ehrRedisTokenStore;
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)
                    .authorizationCodeServices(inMemoryAuthorizationCodeServices)
                    .tokenServices(ehrTokenServices)
                    .tokenStore(ehrRedisTokenStore)
                    //.exceptionTranslator(new EhrOAuth2ExceptionTranslator())
                    .tokenGranter(ehrTokenGranter)
                    .setClientDetailsService(ehrJdbcClientDetailsService);
        }
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.withClientDetails(ehrJdbcClientDetailsService);
        }
        /*@Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.passwordEncoder(new StandardPasswordEncoder());
        }*/
    }
    @Bean
    @Primary
    EhrTokenGranter ehrTokenGranter(
            AuthenticationManager authenticationManager,
            EhrTokenServices tokenServices,
            AuthorizationCodeServices authorizationCodeService,
            ClientDetailsService clientDetailsService,
            EhrJdbcUserSecurityService ehrJDBCUserSecurityService,
            EhrUserDetailsService ehrUserDetailsService,
            EhrRedisVerifyCodeService ehrRedisVerifyCodeService) {
        EhrTokenGranter tokenGranter = new EhrTokenGranter(
                authenticationManager,
                tokenServices,
                authorizationCodeService,
                clientDetailsService,
                new DefaultOAuth2RequestFactory(clientDetailsService),
                ehrJDBCUserSecurityService,
                ehrUserDetailsService,
                ehrRedisVerifyCodeService);
        return tokenGranter;
    }
    @Bean
    @Primary
    RedisTemplate<String, Serializable> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Serializable.class));
        redisTemplate.setHashValueSerializer(new GenericToStringSerializer<>(Long.class));
        return redisTemplate;
    }
    /**
     * jdbc client服务类
     *
     * @return
     */
    @Bean
    @Primary
    EhrJdbcClientDetailsService ehrJdbcClientDetailsService() {
        return new EhrJdbcClientDetailsService(dataSource);
    }
    /**
     * ehr jdbc store code
     *
     * @return
    @Bean
    EhrJDBCAuthorizationCodeService ehrJDBCAuthorizationCodeService() {
        return new EhrJDBCAuthorizationCodeService(dataSource);
    }
     */
    /**
     * memory store code
     * @return
     */
    @Bean
    @Primary
    InMemoryAuthorizationCodeServices inMemoryAuthorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }
    /**
     * jdbc store access_token
     * @return
     */
    /*@Bean
    EhrJdbcTokenStore ehrJdbcTokenStore() {
        EhrJdbcTokenStore tokenStoreService = new EhrJdbcTokenStore(dataSource);
        return tokenStoreService;
    }*/
    /**
     * redis store access_token
     * @return
     */
    @Bean
    @Primary
    EhrRedisTokenStore ehrRedisTokenStore(JedisConnectionFactory jedisConnectionFactory) {
        return  new EhrRedisTokenStore(jedisConnectionFactory);
    }
    @Bean
    @Primary
    EhrTokenServices ehrTokenServices(
            ClientDetailsService ehrJdbcClientDetailsService,
            TokenStore ehrRedisTokenStore,
            JdbcTemplate jdbcTemplate) {
        EhrTokenServices tokenServices = new EhrTokenServices();
        tokenServices.setReuseRefreshToken(true);
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(ehrRedisTokenStore);
        tokenServices.setJdbcTemplate(jdbcTemplate);
        tokenServices.setClientDetailsService(ehrJdbcClientDetailsService);
        return tokenServices;
    }
    @Bean
    @Primary
    EhrJdbcAccessUrlService ehrJDBCAccessUrlService(DataSource dataSource) {
        return new EhrJdbcAccessUrlService(dataSource);
    }
    @Bean
    @Primary
    EhrJdbcUserSecurityService ehrJDBCUserSecurityService(DataSource dataSource) {
        return new EhrJdbcUserSecurityService(dataSource);
    }
    @Bean
    EhrRedisVerifyCodeService ehrRedisVerifyCodeService(RedisTemplate<String, Serializable> redisTemplate) {
        return new EhrRedisVerifyCodeService(redisTemplate);
    }
    //@Configuration
    // @EnableResourceServer
    public static class ResourceServer extends ResourceServerConfigurerAdapter {
        @Autowired
        private EhrTokenServices tokenServices;
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatcher(
                            new OrRequestMatcher(
                                    new AntPathRequestMatcher("/api/v1.0/**")
                            ))
                    .authorizeRequests()
                    .antMatchers("/api/v1.0/**").access("#oauth2.hasScope('read')");
        }
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources
                    .resourceId(RESOURCE_ID)
                    .tokenServices(tokenServices)
                    .tokenExtractor(new EhrTokenExtractor())
                    .authenticationEntryPoint(new EhrOAuth2AuthenticationEntryPoint());
        }
    }
}

+ 117 - 0
src/main/java/com/yihu/ehr/oauth2/config/SecurityConfig.java

@ -0,0 +1,117 @@
package com.yihu.ehr.oauth2.config;
import com.yihu.ehr.oauth2.oauth2.EhrUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
/**
 * @author Sand
 * @version 1.0
 * @created 2016.03.03 21:57
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private EhrUserDetailsService userDetailsService;
    @Autowired
    private Md5PasswordEncoder md5PasswordEncoder;
    @Order(1)
    @Configuration
    @EnableWebSecurity
    public static class AuthorizationApiSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private AuthenticationProvider authenticationProvider;
        @Override
        protected void configure(AuthenticationManagerBuilder authBuilder) throws Exception {
            authBuilder.authenticationProvider(authenticationProvider);
            //authBuilder.userDetailsService(userDetailsService).passwordEncoder(md5PasswordEncoder);
        }
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring()
                    //swagger-ui界面---start
                    .antMatchers("/swagger-ui.html")
                    .antMatchers("/swagger-resources/**")
                    .antMatchers("/v2/api-docs/**")
                    .antMatchers("/configuration/**")
                    .antMatchers("/webjars/springfox-swagger-ui/**")
                    //swagger-ui界面---end
                    .antMatchers("/oauth/sso")
                    .antMatchers("/oauth/login")
                    .antMatchers("/oauth/logout")
                    .antMatchers("/oauth/authorize")
                    .antMatchers("/oauth/accessToken")
                    .antMatchers("/oauth/validToken")
                    .antMatchers("/oauth/confirm_access")
                    .antMatchers("/oauth/verifyCodeValidate")
                    .antMatchers("/oauth/verifyCode")
                    .antMatchers("/oauth/verifyCodeExpire");
        }
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().requestMatcher(
                    new OrRequestMatcher(
                            new AntPathRequestMatcher("/api/v*/authorizations/**"),
                            new AntPathRequestMatcher("/api/v*/applications/*/tokens/*")
                    ))
                    .authorizeRequests()
                    .antMatchers("/api/v*/authorizations/**").hasAnyRole("USER", "CLIENT")
                    .antMatchers("/api/v*/applications/*/tokens/*").hasAnyRole("USER", "CLIENT")
                    .and()
                    .httpBasic();
        }
    }
    @Bean
    public Md5PasswordEncoder passwordEncoder() {
        Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
        return passwordEncoder;
    }
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(md5PasswordEncoder);
        return authenticationProvider;
    }
//    /**
//     * 临时使用的配置,启用全部URI匿名访问。
//     */
//    @Order(1)
//    @Configuration
//    public static class TempSecurityConfig extends WebSecurityConfigurerAdapter {
//        @Autowired
//        private EhrUserDetailsService userDetailsService;
//
//        @Override
//        protected void configure(AuthenticationManagerBuilder authBuilder) throws Exception {
//            authBuilder.userDetailsService(userDetailsService).passwordEncoder(new Md5PasswordEncoder());
//        }
//
//        @Override
//        protected void configure(HttpSecurity http) throws Exception {
//            http.csrf().disable().antMatcher("/**").authorizeRequests().anyRequest().permitAll();
//        }
//    }
}

+ 75 - 0
src/main/java/com/yihu/ehr/oauth2/interceptor/RateLimitInterceptor.java

@ -0,0 +1,75 @@
package com.yihu.ehr.oauth2.interceptor;
import com.yihu.ehr.constants.ErrorCode;
import com.yihu.ehr.constants.HttpHeader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * API请求频率截取器。频率限制策略:
 * - 认证过的客户端,5000次/小时
 * - 未授权的客户端,60次/小时
 *
 * @author Sand
 * @version 1.0
 * @created 2016.02.26 15:57
 */
//@Component
public class RateLimitInterceptor extends HandlerInterceptorAdapter {
    private static final int AuthorizedRateLimit = 5000;
    private static final int UnauthorizedRateLimit = 60;
    @Autowired
    private RateLimitService rateLimitService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean authorized = isAuthorized(getAccessToken(request));
        int count = rateLimitService.incrementLimit(request.getRemoteAddr());
        int limit = authorized ? AuthorizedRateLimit : UnauthorizedRateLimit;
        boolean exceeding = authorized ? count > AuthorizedRateLimit : count > UnauthorizedRateLimit;
        response.setIntHeader(HttpHeader.RATE_LIMIT_LIMIT, limit);
        response.setHeader(HttpHeader.RATE_LIMIT_RESET, Long.toString(rateLimitService.getResetTime(request.getRemoteAddr())));
        response.setIntHeader(HttpHeader.RATE_LIMIT_REMAINING, exceeding ? 0 : limit - count);
        if (exceeding) {
            response.setStatus(HttpStatus.FORBIDDEN.value());
            return false;
        }
        return true;
    }
    private boolean isAuthorized(String token){
        if (StringUtils.isEmpty(token)) return false;
        // TODO 检查是否已授权
        return false;
    }
    /**
     * 兼容OAuth Token与Swagger api key。
     *
     * @param request
     * @return
     */
    private String getAccessToken(HttpServletRequest request){
        String token = request.getHeader(HttpHeader.AUTHORIZATION);
        if (StringUtils.isEmpty(token)){
            token = request.getParameter("access_token");
        }
        if (StringUtils.isEmpty(token)){
            token = request.getParameter("api_key");
        }
        return token;
    }
}

+ 42 - 0
src/main/java/com/yihu/ehr/oauth2/interceptor/RateLimitService.java

@ -0,0 +1,42 @@
package com.yihu.ehr.oauth2.interceptor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
/**
 * Created by Sand Wen on 2016.2.27.
 */
//@Component
public class RateLimitService {
    public static int timeToNextQuarter = 15;
    private Calendar calendar = Calendar.getInstance();
    @Scheduled(cron = "0/10 * * * * *")
    public void updateExpireTimeForCache() {
        int currentMinute = calendar.get(Calendar.MINUTE);
        int mod = currentMinute % 15;
        calendar.set(Calendar.MINUTE, currentMinute - mod + 15);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        timeToNextQuarter = (int) ((calendar.getTime().getTime() - new Date().getTime())/1000);
    }
    static int test = 0;
    public int incrementLimit(String ip) {
        return ++test;
    }
    public long getResetTime(String ip){
        return System.currentTimeMillis()/1000 + 60;
    }
    public void reset(String ip){
    }
}

+ 33 - 0
src/main/java/com/yihu/ehr/oauth2/interceptor/UserAgentInterceptor.java

@ -0,0 +1,33 @@
package com.yihu.ehr.oauth2.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * User-Agent检查器.
 *
 * @author Sand
 * @version 1.0
 * @created 2016.02.26 18:04
 */
//@Component
public class UserAgentInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userAgent = request.getHeader("User-Agent");
        if (!StringUtils.isEmpty(userAgent)) {
            if (userAgent.contains("Mozilla")) {
                return true;
            } else if (userAgent.startsWith("user ") || userAgent.startsWith("client ")) {
                return true;
            }
        }
        return true;
    }
}

+ 63 - 0
src/main/java/com/yihu/ehr/oauth2/model/AccessToken.java

@ -0,0 +1,63 @@
package com.yihu.ehr.oauth2.model;
import java.io.Serializable;
/**
 * Created by hzp on 2017/4/27.
 */
public class AccessToken implements Serializable {
    private String accessToken;
    private String refreshToken;
    private String tokenType;
    private String user;
    private int expiresIn;
    private String state;
    public String getTokenType() {
        return tokenType;
    }
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public String getRefreshToken() {
        return refreshToken;
    }
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}

+ 250 - 0
src/main/java/com/yihu/ehr/oauth2/model/EhrUserDetails.java

@ -0,0 +1,250 @@
package com.yihu.ehr.oauth2.model;
import java.util.Date;
/**
 * @author Sand
 * @version 1.0
 * @updated 02-6月-2015 20:25:02
 */
public class EhrUserDetails {
    private String id;
    private Date createDate;
    private Date lastLoginTime;
    private String email;
    private String loginCode;
    private String realName;
    private String password;
    private Boolean activated;
    private String validateCode;
    private String userType;
    private String telephone;
    private String martialStatus;
    private String gender;
    private String idCardNo;
    private String organization;
    private String source;
    // 未绑定属性
	private String orgPost;
	private String privilege;
	private String role;
    //特殊属性
    private String major;
    private String medicalRole;
    private String techTitle;
    private String adminTitle;
    private String DType;
    private String imgRemotePath;
    private String imgLocalPath;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public Date getCreateDate() {
        return createDate;
    }
    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
    public Date getLastLoginTime() {
        return lastLoginTime;
    }
    public void setLastLoginTime(Date lastLoginTime) {
        this.lastLoginTime = lastLoginTime;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getLoginCode() {
        return loginCode;
    }
    public void setLoginCode(String loginCode) {
        this.loginCode = loginCode;
    }
    public String getRealName() {
        return realName;
    }
    public void setRealName(String realName) {
        this.realName = realName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Boolean getActivated() {
        return activated;
    }
    public void setActivated(Boolean activated) {
        this.activated = activated;
    }
    public String getValidateCode() {
        return validateCode;
    }
    public void setValidateCode(String validateCode) {
        this.validateCode = validateCode;
    }
    public String getUserType() {
        return userType;
    }
    public void setUserType(String userType) {
        this.userType = userType;
    }
    public String getTelephone() {
        return telephone;
    }
    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }
    public String getMartialStatus() {
        return martialStatus;
    }
    public void setMartialStatus(String martialStatus) {
        this.martialStatus = martialStatus;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public String getIdCardNo() {
        return idCardNo;
    }
    public void setIdCardNo(String idCardNo) {
        this.idCardNo = idCardNo;
    }
    public String getOrganization() {
        return organization;
    }
    public void setOrganization(String organization) {
        this.organization = organization;
    }
    public String getSource() {
        return source;
    }
    public void setSource(String source) {
        this.source = source;
    }
    public String getOrgPost() {
        return orgPost;
    }
    public void setOrgPost(String orgPost) {
        this.orgPost = orgPost;
    }
    public String getPrivilege() {
        return privilege;
    }
    public void setPrivilege(String privilege) {
        this.privilege = privilege;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
    public String getMajor() {
        return major;
    }
    public void setMajor(String major) {
        this.major = major;
    }
    public String getMedicalRole() {
        return medicalRole;
    }
    public void setMedicalRole(String medicalRole) {
        this.medicalRole = medicalRole;
    }
    public String getTechTitle() {
        return techTitle;
    }
    public void setTechTitle(String techTitle) {
        this.techTitle = techTitle;
    }
    public String getAdminTitle() {
        return adminTitle;
    }
    public void setAdminTitle(String adminTitle) {
        this.adminTitle = adminTitle;
    }
    public String getDType() {
        return DType;
    }
    public void setDType(String DType) {
        this.DType = DType;
    }
    public String getImgRemotePath() {
        return imgRemotePath;
    }
    public void setImgRemotePath(String imgRemotePath) {
        this.imgRemotePath = imgRemotePath;
    }
    public String getImgLocalPath() {
        return imgLocalPath;
    }
    public void setImgLocalPath(String imgLocalPath) {
        this.imgLocalPath = imgLocalPath;
    }
}

+ 38 - 0
src/main/java/com/yihu/ehr/oauth2/model/Oauth2Failed.java

@ -0,0 +1,38 @@
package com.yihu.ehr.oauth2.model;
import java.io.Serializable;
/**
 * Created by progr1mmer on 2018/4/21.
 */
public class Oauth2Failed implements Serializable {
    private int errorCode;
    private String errorMsg;
    public Oauth2Failed(){
    }
    public Oauth2Failed(int errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
    public int getErrorCode() {
        return errorCode;
    }
    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }
    public String getErrorMsg() {
        return errorMsg;
    }
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

+ 45 - 0
src/main/java/com/yihu/ehr/oauth2/model/VerifyCode.java

@ -0,0 +1,45 @@
package com.yihu.ehr.oauth2.model;
import java.io.Serializable;
/**
 * Created by progr1mmer on 2018/4/18.
 */
public class VerifyCode implements Serializable {
    /**
     * 验证码
     */
    private String code;
    /**
     * 验证码有限期
     */
    private int expiresIn;
    /**
     * 验证码下一次请求时间
     */
    private int nextRequestTime;
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
    public int getNextRequestTime() {
        return nextRequestTime;
    }
    public void setNextRequestTime(int nextRequestTime) {
        this.nextRequestTime = nextRequestTime;
    }
}

+ 76 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrOAuth2AuthenticationEntryPoint.java

@ -0,0 +1,76 @@
package com.yihu.ehr.oauth2.oauth2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.AbstractOAuth2SecurityExceptionHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 参见 {@link OAuth2AuthenticationEntryPoint}。此入口点为Security过滤器认证出错时被调用,并对返回值进行格式化。
 *
 * 本入口点仅返回401错误。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.07 17:46
 */
public class EhrOAuth2AuthenticationEntryPoint extends AbstractOAuth2SecurityExceptionHandler implements
        AuthenticationEntryPoint {
    private String typeName = OAuth2AccessToken.BEARER_TYPE;
    private String realmName = "oauth";
    public void setRealmName(String realmName) {
        this.realmName = realmName;
    }
    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {
        doHandle(request, response, authException);
    }
    @Override
    protected ResponseEntity<OAuth2Exception> enhanceResponse(ResponseEntity<OAuth2Exception> response, Exception exception) {
        HttpHeaders headers = response.getHeaders();
        String existing = null;
        if (headers.containsKey("WWW-Authenticate")) {
            existing = extractTypePrefix(headers.getFirst("WWW-Authenticate"));
        }
        StringBuilder builder = new StringBuilder();
        builder.append(typeName+" ");
        builder.append("realm=\"" + realmName + "\"");
        if (existing!=null) {
            builder.append(", "+existing);
        }
        HttpHeaders update = new HttpHeaders();
        update.putAll(response.getHeaders());
        update.set("WWW-Authenticate", builder.toString());
        return new ResponseEntity<OAuth2Exception>(response.getBody(), update, response.getStatusCode());
    }
    private String extractTypePrefix(String header) {
        String existing = header;
        String[] tokens = existing.split(" +");
        if (tokens.length > 1 && !tokens[0].endsWith(",")) {
            existing = StringUtils.arrayToDelimitedString(tokens, " ").substring(existing.indexOf(" ") + 1);
        }
        return existing;
    }
}

+ 153 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrOAuth2ExceptionTranslator.java

@ -0,0 +1,153 @@
package com.yihu.ehr.oauth2.oauth2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import java.io.IOException;
/**
 * OAuth2异常转换。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.05 17:07
 */
@Component
public class EhrOAuth2ExceptionTranslator implements WebResponseExceptionTranslator {
    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        // Try to extract a SpringSecurityException from the stacktrace
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
        Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(
                OAuth2Exception.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception((OAuth2Exception) ase);
        }
        ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,
                causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e));
        }
        ase = (AccessDeniedException) throwableAnalyzer
                .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase instanceof AccessDeniedException) {
            return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
        }
        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer
                .getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase instanceof HttpRequestMethodNotSupportedException) {
            return handleOAuth2Exception(new MethodNotAllowed(ase.getMessage(), ase));
        }
        return handleOAuth2Exception(new ServerErrorException(e.getMessage(), e));
    }
    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
            headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
        }
        ResponseEntity<OAuth2Exception> response = new ResponseEntity<>(e, headers,
                HttpStatus.valueOf(status));
        return response;
    }
    public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
        this.throwableAnalyzer = throwableAnalyzer;
    }
    @SuppressWarnings("serial")
    private static class ForbiddenException extends OAuth2Exception {
        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "access_denied";
        }
        public int getHttpErrorCode() {
            return 403;
        }
    }
    @SuppressWarnings("serial")
    private static class ServerErrorException extends OAuth2Exception {
        public ServerErrorException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "server_error";
        }
        public int getHttpErrorCode() {
            return 500;
        }
    }
    @SuppressWarnings("serial")
    private static class UnauthorizedException extends OAuth2Exception {
        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }
        public int getHttpErrorCode() {
            return 401;
        }
    }
    @SuppressWarnings("serial")
    private static class MethodNotAllowed extends OAuth2Exception {
        public MethodNotAllowed(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }
        public int getHttpErrorCode() {
            return 405;
        }
    }
}

+ 90 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenExtractor.java

@ -0,0 +1,90 @@
package com.yihu.ehr.oauth2.oauth2;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
 * 从请求中提取Token。兼容Swagger Api Key。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.07 19:32
 */
public class EhrTokenExtractor implements TokenExtractor {
    private final static Log logger = LogFactory.getLog(EhrTokenExtractor.class);
    private final static String SWAGGER_API_KEY = "api_key";
    @Override
    public Authentication extract(HttpServletRequest request) {
        String tokenValue = extractToken(request);
        if (tokenValue != null) {
            PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, "");
            return authentication;
        }
        return null;
    }
    protected String extractToken(HttpServletRequest request) {
        String token = extractHeaderToken(request);
        // bearer 类型Token可能在请求参数中
        if (token == null) {
            logger.debug("请求头不包含Token,尝试从请求参数中查找...");
            token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
            if (token == null) {
                token = request.getParameter(SWAGGER_API_KEY);
                if (token == null) {
                    logger.debug("找不到Token,非OAuth2请求类型.");
                }
            } else {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
            }
        }
        return token;
    }
    /**
     * 从请求头中提取Token。
     *
     * @param request The request.
     * @return Token,若没有则返回null
     */
    protected String extractHeaderToken(HttpServletRequest request) {
        String apiKey = request.getHeader(SWAGGER_API_KEY);
        if (apiKey != null) return apiKey;
        Enumeration<String> headers = request.getHeaders(SWAGGER_API_KEY);
        if(headers == null){
            headers = request.getHeaders("Authorization");
        }
        while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
            String value = headers.nextElement();
            if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
                String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
                int commaIndex = authHeaderValue.indexOf(',');
                if (commaIndex > 0) {
                    authHeaderValue = authHeaderValue.substring(0, commaIndex);
                }
                return authHeaderValue;
            }
        }
        return null;
    }
}

+ 355 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenGranter.java

@ -0,0 +1,355 @@
package com.yihu.ehr.oauth2.oauth2;
import com.yihu.ehr.oauth2.oauth2.jdbc.EhrJdbcUserSecurityService;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisVerifyCodeService;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitGrantService;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.*;
/**
 * Token授权器,根据请求创建Token给客户端。
 *
 * @author Progr1mmer
 * @created 2018.01.09
 */
public class EhrTokenGranter implements TokenGranter {
    private final Map<String, TokenGranter> tokenGranters = new HashMap<>();
    public EhrTokenGranter(AuthenticationManager authenticationManager,
                           AuthorizationServerTokenServices tokenServices,
                           AuthorizationCodeServices authorizationCodeServices,
                           ClientDetailsService clientDetailsService,
                           OAuth2RequestFactory requestFactory,
                           EhrJdbcUserSecurityService ehrJDBCUserSecurityService,
                           EhrUserDetailsService ehrUserDetailsService,
                           EhrRedisVerifyCodeService ehrRedisVerifyCodeService) {
        tokenGranters.put(EhrAuthorizationCodeGranter.GRANT_TYPE,
                new EhrAuthorizationCodeGranter(
                        tokenServices,
                        authorizationCodeServices,
                        clientDetailsService,
                        requestFactory,
                        ehrJDBCUserSecurityService,
                        ehrUserDetailsService
                ));
        tokenGranters.put(EhrResourceOwnerPasswordTokenGranter.GRANT_TYPE,
                new EhrResourceOwnerPasswordTokenGranter(
                        authenticationManager,
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrRefreshTokenGranter.GRANT_TYPE,
                new EhrRefreshTokenGranter(
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrImplicitTokenGranter.GRANT_TYPE,
                new EhrImplicitTokenGranter(
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrVerifyCodeTokenGranter.GRANT_TYPE,
                new EhrVerifyCodeTokenGranter(
                        authenticationManager,
                        tokenServices,
                        clientDetailsService,
                        requestFactory,
                        ehrRedisVerifyCodeService
                ));
    }
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        if (tokenGranters.get(grantType) != null) {
            OAuth2AccessToken grant = tokenGranters.get(grantType).grant(grantType, tokenRequest);
            return grant;
        }
        return null;
    }
    /**
     * authorization_code模式Token授权器。
     */
    public static class EhrAuthorizationCodeGranter extends AbstractTokenGranter {
        public static final String GRANT_TYPE = "authorization_code";
        private final AuthorizationCodeServices authorizationCodeServices;
        // Ehr Properties
        private final EhrJdbcUserSecurityService ehrJDBCUserSecurityService;
        private final EhrUserDetailsService ehrUserDetailsService;
        public EhrAuthorizationCodeGranter(AuthorizationServerTokenServices tokenServices,
                                           AuthorizationCodeServices authorizationCodeServices,
                                           ClientDetailsService clientDetailsService,
                                           OAuth2RequestFactory requestFactory,
                                           EhrJdbcUserSecurityService ehrJDBCUserSecurityService,
                                           EhrUserDetailsService ehrUserDetailsService) {
            this(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory, GRANT_TYPE, ehrJDBCUserSecurityService, ehrUserDetailsService);
        }
        protected EhrAuthorizationCodeGranter(AuthorizationServerTokenServices tokenServices,
                                              AuthorizationCodeServices authorizationCodeServices,
                                              ClientDetailsService clientDetailsService,
                                              OAuth2RequestFactory requestFactory,
                                              String grantType,
                                              EhrJdbcUserSecurityService ehrJDBCUserSecurityService,
                                              EhrUserDetailsService ehrUserDetailsService) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authorizationCodeServices = authorizationCodeServices;
            this.ehrUserDetailsService = ehrUserDetailsService;
            this.ehrJDBCUserSecurityService = ehrJDBCUserSecurityService;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = tokenRequest.getRequestParameters();
            String authorizationCode = parameters.get("code");
            String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);
            if (authorizationCode == null) {
                throw new InvalidRequestException("An authorization code must be supplied.");
            }
            OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
            if (storedAuth == null) {
                throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
            }
            OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();
            String oauthUri;
            if (pendingOAuth2Request.getRedirectUri().indexOf("?") != -1) {
                oauthUri = pendingOAuth2Request.getRedirectUri().substring(0, pendingOAuth2Request.getRedirectUri().indexOf("?"));
            } else {
                oauthUri = pendingOAuth2Request.getRedirectUri();
            }
            // https://jira.springsource.org/browse/SECOAUTH-333
            // This might be null, if the authorization was done without the redirect_uri parameter
            String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(
                    OAuth2Utils.REDIRECT_URI);
            if ((redirectUri != null || redirectUriApprovalParameter != null)
                    && !oauthUri.equals(redirectUri)) {
                throw new RedirectMismatchException("Redirect URI mismatch.");
            }
            String pendingClientId = pendingOAuth2Request.getClientId();
            String clientId = tokenRequest.getClientId();
            if (clientId != null && !clientId.equals(pendingClientId)) {
                // just a sanity check.
                throw new InvalidClientException("Client ID mismatch");
            }
            // Secret is not required in the authorization request, so it won't be available
            // in the pendingAuthorizationRequest. We do want to check that a secret is provided
            // in the token request, but that happens elsewhere.
            Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request
                    .getRequestParameters());
            // Combine the parameters adding the new ones last so they override if there are any clashes
            combinedParameters.putAll(parameters);
            // Make a new stored request with the combined parameters
            OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);
            Authentication userAuth = storedAuth.getUserAuthentication();
            //再次通过pk获取当前请求的用户信息
            /*String pk = combinedParameters.get("pk");
            String keyId = ehrJDBCUserSecurityService.getDefaultKeyIdSelectStatement(pk);
            String userId = ehrJDBCUserSecurityService.getDefaultUserIdByKeyIdSelectStatement(keyId);
            String userName = ehrJDBCUserSecurityService.getDefaultUserNameByUserId(userId);
            UserDetails userDetails = ehrUserDetailsService.loadUserByUsername(userName);
            if (StringUtils.isEmpty(keyId) || StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
                throw new InsufficientAuthenticationException("Illegal pk");
            }
            UsernamePasswordAuthenticationToken userToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());*/
            return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);
        }
    }
    /**
     * password模式Token授权器。
     */
    public static class EhrResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "password";
        private final AuthenticationManager authenticationManager;
        public EhrResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager,
                                                 AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
            this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        protected EhrResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
                                                    ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authenticationManager = authenticationManager;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
            String username = parameters.get("username");
            String password = parameters.get("password");
            // Protect from downstream leaks of password
            parameters.remove("password");
            Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
            ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
            try {
                userAuth = authenticationManager.authenticate(userAuth);
            }
            catch (AccountStatusException ase) {
                //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
                throw new InvalidGrantException(ase.getMessage());
            }
            catch (BadCredentialsException e) {
                // If the username/password are wrong the spec says we should send 400/invalid grant
                throw new InvalidGrantException(e.getMessage());
            }
            if (userAuth == null || !userAuth.isAuthenticated()) {
                throw new InvalidGrantException("Could not authenticate user: " + username);
            }
            OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        }
    }
    /**
     * refresh模式Token授权器。
     */
    public static class EhrRefreshTokenGranter extends AbstractTokenGranter {
        static final String GRANT_TYPE = "refresh_token";
        public EhrRefreshTokenGranter(AuthorizationServerTokenServices tokenServices,
                                      ClientDetailsService clientDetailsService,
                                      OAuth2RequestFactory requestFactory) {
            super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        @Override
        protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
            String refreshToken = tokenRequest.getRequestParameters().get(GRANT_TYPE);
            return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
        }
    }
    /**
     * Implicit模式Token授权器。
     */
    public static class EhrImplicitTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "implicit";
        public EhrImplicitTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
            this(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        protected EhrImplicitTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                                       OAuth2RequestFactory requestFactory, String grantType) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest clientToken) {
            Authentication userAuth = SecurityContextHolder.getContext().getAuthentication();
            if (userAuth == null || !userAuth.isAuthenticated()) {
                throw new InsufficientAuthenticationException("There is no currently logged in user");
            }
            Assert.state(clientToken instanceof ImplicitTokenRequest, "An ImplicitTokenRequest is required here. Caller needs to wrap the TokenRequest.");
            OAuth2Request requestForStorage = ((ImplicitTokenRequest)clientToken).getOAuth2Request();
            return new OAuth2Authentication(requestForStorage, userAuth);
        }
        @SuppressWarnings("deprecation")
        public void setImplicitGrantService(ImplicitGrantService service) {
        }
    }
    /**
     * verify_code模式Token授权器。
     */
    public static class EhrVerifyCodeTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "verify_code";
        private final AuthenticationManager authenticationManager;
        // Ehr Properties
        private final EhrRedisVerifyCodeService ehrRedisVerifyCodeService;
        public EhrVerifyCodeTokenGranter(AuthenticationManager authenticationManager,
                                         AuthorizationServerTokenServices tokenServices,
                                         ClientDetailsService clientDetailsService,
                                         OAuth2RequestFactory requestFactory,
                                         EhrRedisVerifyCodeService ehrRedisVerifyCodeService) {
            this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE, ehrRedisVerifyCodeService);
        }
        protected EhrVerifyCodeTokenGranter(AuthenticationManager authenticationManager,
                                            AuthorizationServerTokenServices tokenServices,
                                            ClientDetailsService clientDetailsService,
                                            OAuth2RequestFactory requestFactory,
                                            String grantType,
                                            EhrRedisVerifyCodeService ehrRedisVerifyCodeService) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authenticationManager = authenticationManager;
            this.ehrRedisVerifyCodeService = ehrRedisVerifyCodeService;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
            String client_id = parameters.get("client_id");
            String username = parameters.get("username");
            String verify_code = parameters.get("verify_code");
            if (!ehrRedisVerifyCodeService.verification(client_id, username, verify_code)){
                throw new InvalidGrantException("Invalid verify_code");
            }
            Authentication userAuth = new UsernamePasswordAuthenticationToken(username, verify_code, getGrantedAuthorities(username));
            ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
            OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        }
        private Collection<? extends GrantedAuthority> getGrantedAuthorities(String username) {
            Collection<GrantedAuthority> authorities = new ArrayList<>(1);
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            return authorities;
        }
    }
}

+ 438 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrTokenServices.java

@ -0,0 +1,438 @@
package com.yihu.ehr.oauth2.oauth2;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.*;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.Assert;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.Set;
import java.util.UUID;
/**
 *
 * 使用随机UUID值作为access token及refresh token。本实现修改自 {@link DefaultTokenServices},
 * 并取消{@link TokenEnhancer}对Token的二次处理。原功能中使用{@link TokenEnhancer}作为在生成Token后,
 * 在存储之前对Token对二次处理,新实现直接修改生成逻辑。
 *
 * Token存储由 {@link TokenStore} 接口的子类实现。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.05 15:30
 */
public class EhrTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean {
    private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
    private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
    private boolean supportRefreshToken = false;
    private boolean reuseRefreshToken = true;
    private TokenStore tokenStore;
    private ClientDetailsService clientDetailsService;
    private TokenEnhancer accessTokenEnhancer;
    private AuthenticationManager authenticationManager;
    private JdbcTemplate jdbcTemplate;
    /**
     * Initialize these token services. If no random generator is set, one will be created.
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(tokenStore, "tokenStore ot set");
    }
    @Override
    public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
        OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
        OAuth2RefreshToken refreshToken = null;
        if (existingAccessToken != null) {
            if (existingAccessToken.isExpired()) {
                if (existingAccessToken.getRefreshToken() != null) {
                    refreshToken = existingAccessToken.getRefreshToken();
                    // The token store could remove the refresh token when the
                    // access token is removed, but we want to
                    // be sure...
                    tokenStore.removeRefreshToken(refreshToken);
                }
                tokenStore.removeAccessToken(existingAccessToken);
            } else {
                // Re-store the access token in case the authentication has changed
                tokenStore.storeAccessToken(existingAccessToken, authentication);
                return existingAccessToken;
            }
        }
        // Only create a new refresh token if there wasn't an existing one
        // associated with an expired access token.
        // Clients might be holding existing refresh tokens, so we re-use it in
        // the case that the old access token
        // expired.
        if (refreshToken == null) {
            refreshToken = createRefreshToken(authentication);
        }
        // But the refresh token itself might need to be re-issued if it has
        // expired.
        else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
            ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
            if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
                refreshToken = createRefreshToken(authentication);
            }
        }
        OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
        tokenStore.storeAccessToken(accessToken, authentication);
        // In case it was modified
        refreshToken = accessToken.getRefreshToken();
        if (refreshToken != null) {
            tokenStore.storeRefreshToken(refreshToken, authentication);
        }
        return accessToken;
    }
    @Override
    public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
            throws AuthenticationException {
        if (!supportRefreshToken) {
            throw new InvalidTokenException("Invalid refresh token: " + refreshTokenValue);
        }
        OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
        if (refreshToken == null) {
            throw new InvalidTokenException("Invalid refresh token: " + refreshTokenValue);
        }
        OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
        if (this.authenticationManager != null && !authentication.isClientOnly()) {
            // The client has already been authenticated, but the user authentication might be old now, so give it a
            // chance to re-authenticate.
            Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
            user = authenticationManager.authenticate(user);
            Object details = authentication.getDetails();
            authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
            authentication.setDetails(details);
        }
        String clientId = authentication.getOAuth2Request().getClientId();
        if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
            throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
        }
        // clear out any access tokens already associated with the refresh
        // token.
        tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
        if (isExpired(refreshToken)) {
            tokenStore.removeRefreshToken(refreshToken);
            throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
        }
        authentication = createRefreshedAuthentication(authentication, tokenRequest);
        if (!reuseRefreshToken) {
            tokenStore.removeRefreshToken(refreshToken);
            refreshToken = createRefreshToken(authentication);
        }
        OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
        tokenStore.storeAccessToken(accessToken, authentication);
        if (!reuseRefreshToken) {
            tokenStore.storeRefreshToken(refreshToken, authentication);
        }
        return accessToken;
    }
    @Override
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        return tokenStore.getAccessToken(authentication);
    }
    /**
     * Create a refreshed authentication.
     *
     * @param authentication The authentication.
     * @param request        The scope for the refreshed token.
     * @return The refreshed authentication.
     * @throws InvalidScopeException If the scope requested is invalid or wider than the original scope.
     */
    private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) {
        OAuth2Authentication narrowed = authentication;
        Set<String> scope = request.getScope();
        OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
        if (scope != null && !scope.isEmpty()) {
            Set<String> originalScope = clientAuth.getScope();
            if (originalScope == null || !originalScope.containsAll(scope)) {
                throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope
                        + ".", originalScope);
            } else {
                clientAuth = clientAuth.narrowScope(scope);
            }
        }
        narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
        return narrowed;
    }
    protected boolean isExpired(OAuth2RefreshToken refreshToken) {
        if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
            ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken;
            return expiringToken.getExpiration() == null
                    || System.currentTimeMillis() > expiringToken.getExpiration().getTime();
        }
        return false;
    }
    public OAuth2AccessToken readAccessToken(String accessToken) {
        return tokenStore.readAccessToken(accessToken);
    }
    public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
            InvalidTokenException {
        OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
        if (accessToken == null) {
            throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
        } else if (accessToken.isExpired()) {
            tokenStore.removeAccessToken(accessToken);
            throw new InvalidTokenException("Access token expired: " + accessTokenValue);
        }
        OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
        if (result == null) {
            // in case of race condition
            throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
        }
        if (clientDetailsService != null) {
            String clientId = result.getOAuth2Request().getClientId();
            try {
                clientDetailsService.loadClientByClientId(clientId);
            } catch (ClientRegistrationException e) {
                throw new InvalidTokenException("Client not valid: " + clientId, e);
            }
        }
        return result;
    }
    public String getClientId(String tokenValue) {
        OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
        if (authentication == null) {
            throw new InvalidTokenException("Invalid access token: " + tokenValue);
        }
        OAuth2Request clientAuth = authentication.getOAuth2Request();
        if (clientAuth == null) {
            throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
        }
        return clientAuth.getClientId();
    }
    public boolean revokeToken(String tokenValue) {
        OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
        if (accessToken == null) {
            return false;
        }
        if (accessToken.getRefreshToken() != null) {
            tokenStore.removeRefreshToken(accessToken.getRefreshToken());
        }
        tokenStore.removeAccessToken(accessToken);
        return true;
    }
    private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
        if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
            return null;
        }
        int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
        String value = UUID.randomUUID().toString();
        if (validitySeconds > 0) {
            return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis()
                    + (validitySeconds * 1000L)));
        }
        return new DefaultOAuth2RefreshToken(value);
    }
    private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
        int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
        if (validitySeconds > 0) {
            token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
        }
        token.setRefreshToken(refreshToken);
        token.setScope(authentication.getOAuth2Request().getScope());
        return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
    }
    /**
     * The access token validity period in seconds
     *
     * @param clientAuth the current authorization request
     * @return the access token validity period in seconds
     */
    protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
        if (clientDetailsService != null) {
            ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
            Integer validity = client.getAccessTokenValiditySeconds();
            if (validity != null) {
                return validity;
            }
        }
        return accessTokenValiditySeconds;
    }
    /**
     * The refresh token validity period in seconds
     *
     * @param clientAuth the current authorization request
     * @return the refresh token validity period in seconds
     */
    protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) {
        if (clientDetailsService != null) {
            ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
            Integer validity = client.getRefreshTokenValiditySeconds();
            if (validity != null) {
                return validity;
            }
        }
        return refreshTokenValiditySeconds;
    }
    /**
     * Is a refresh token supported for this client (or the global setting if
     * {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not set.
     *
     * @param clientAuth the current authorization request
     * @return boolean to indicate if refresh token is supported
     */
    protected boolean isSupportRefreshToken(OAuth2Request clientAuth) {
        if (clientDetailsService != null) {
            ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
            return client.getAuthorizedGrantTypes().contains("refresh_token");
        }
        return this.supportRefreshToken;
    }
    /**
     * An access token enhancer that will be applied to a new token before it is saved in the token store.
     *
     * @param accessTokenEnhancer the access token enhancer to set
     */
    public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
        this.accessTokenEnhancer = accessTokenEnhancer;
    }
    /**
     * The validity (in seconds) of the refresh token. If less than or equal to zero then the tokens will be
     * non-expiring.
     *
     * @param refreshTokenValiditySeconds The validity (in seconds) of the refresh token.
     */
    public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
        this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
    }
    /**
     * The default validity (in seconds) of the access token. Zero or negative for non-expiring tokens. If a client
     * details service is set the validity period will be read from he client, defaulting to this value if not defined
     * by the client.
     *
     * @param accessTokenValiditySeconds The validity (in seconds) of the access token.
     */
    public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
        this.accessTokenValiditySeconds = accessTokenValiditySeconds;
    }
    /**
     * Whether to support the refresh token.
     *
     * @param supportRefreshToken Whether to support the refresh token.
     */
    public void setSupportRefreshToken(boolean supportRefreshToken) {
        this.supportRefreshToken = supportRefreshToken;
    }
    /**
     * Whether to reuse refresh tokens (until expired).
     *
     * @param reuseRefreshToken Whether to reuse refresh tokens (until expired).
     */
    public void setReuseRefreshToken(boolean reuseRefreshToken) {
        this.reuseRefreshToken = reuseRefreshToken;
    }
    /**
     * The persistence strategy for token storage.
     *
     * @param tokenStore the store for access and refresh tokens.
     */
    public void setTokenStore(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }
    /**
     * An authentication manager that will be used (if provided) to check the user authentication when a token is
     * refreshed.
     *
     * @param authenticationManager the authenticationManager to set
     */
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }
    /**
     * The client details service to use for looking up clients (if necessary). Optional if the access token expiry is
     * set globally via {@link #setAccessTokenValiditySeconds(int)}.
     *
     * @param clientDetailsService the client details service
     */
    public void setClientDetailsService(ClientDetailsService clientDetailsService) {
        this.clientDetailsService = clientDetailsService;
    }
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    public OAuth2AccessToken getAccessTokenByClientId(String client_id) {
        OAuth2AccessToken accessToken = null;
        String sql="select token_id, token,max(create_time)  from oauth_access_token where client_id = ? GROUP BY token_id, token,create_time ";
        try {
            accessToken = jdbcTemplate.queryForObject(sql,
                    new RowMapper<OAuth2AccessToken>() {
                        public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return deserializeAccessToken(rs.getBytes(2));
                        }
                    }, client_id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return accessToken;
    }
    protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
        return SerializationUtils.deserialize(token);
    }
}

+ 68 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/EhrUserDetailsService.java

@ -0,0 +1,68 @@
package com.yihu.ehr.oauth2.oauth2;
import com.yihu.ehr.model.user.EhrUserSimple;
import com.yihu.ehr.oauth2.model.EhrUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
 * @author Sand
 * @version 1.0
 * @created 2016.03.02 17:11
 */
@Service
public class EhrUserDetailsService implements UserDetailsService {
    private static final String DEFAULT_USER_DETAILS_STATEMENT = "SELECT * FROM users u WHERE u.login_code = ? OR u.telephone = ? OR u.id_card_no = ?";
    @Autowired
    private JdbcTemplate jdbcTemplate;
    /**
     * 用户登录判读接口
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<EhrUserDetails> users = jdbcTemplate.query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(EhrUserDetails.class), username, username, username);
        if (users == null || users.size() == 0) {
            throw new UsernameNotFoundException(username);
        }
        return new User(username, users.get(0).getPassword(), getGrantedAuthorities(username));
        /*if (username.equals("admin")){
            return new User("admin", "e10adc3949ba59abbe56e057f20f883e", getGrantedAuthorities(username));
        }
        return null;*/
    }
    private Collection<? extends GrantedAuthority> getGrantedAuthorities(String username) {
        Collection<GrantedAuthority> authorities = new ArrayList<>(1);
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return authorities;
    }
    public EhrUserSimple loadUserSimpleByUsername(String username) throws UsernameNotFoundException {
        List<EhrUserSimple> users = jdbcTemplate.query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(EhrUserSimple.class), username, username, username);
        if (users == null || users.size() == 0) {
            throw new UsernameNotFoundException(username);
        }
        return users.get(0);
    }
}

+ 57 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryAuthorizationCodeService.java

@ -0,0 +1,57 @@
package com.yihu.ehr.oauth2.oauth2.inMemory;
import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import java.util.concurrent.ConcurrentHashMap;
/**
 * OAuth2验证码服务
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.05 11:03
 */
public class EhrInMemoryAuthorizationCodeService extends RandomValueAuthorizationCodeServices {
    protected final ConcurrentHashMap<String, OAuth2Authentication> authorizationCodeStore = new ConcurrentHashMap<String, OAuth2Authentication>();
    @Override
    protected void store(String code, OAuth2Authentication authentication) {
        this.authorizationCodeStore.put(code, authentication);
    }
    @Override
    public OAuth2Authentication remove(String code) {
        OAuth2Authentication auth = this.authorizationCodeStore.remove(code);
        return auth;
    }
    @Override
    public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException {
        OAuth2Authentication auth = this.remove(code);
        if (auth == null) {
            throw new BadVerificationCodeException("授权码: " + code + "无效或已过期");
        }
        return auth;
    }
    static class BadVerificationCodeException extends ClientAuthenticationException {
        public BadVerificationCodeException(String msg, Throwable t) {
            super(msg, t);
        }
        public BadVerificationCodeException(String msg) {
            super(msg);
        }
        @Override
        public String getOAuth2ErrorCode() {
            return "无效验证码";
        }
    }
}

+ 53 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryClientDetailsService.java

@ -0,0 +1,53 @@
//package com.yihu.ehr.oauth2.oauth2.inMemory;
//
//import com.yihu.ehr.model.app.MApp;
//import com.yihu.ehr.oauth2.feign.AppClient;
//import com.yihu.ehr.oauth2.oauth2.EhrTokenGranter;
//import org.springframework.security.oauth2.provider.ClientDetails;
//import org.springframework.security.oauth2.provider.ClientDetailsService;
//import org.springframework.security.oauth2.provider.ClientRegistrationException;
//import org.springframework.security.oauth2.provider.NoSuchClientException;
//import org.springframework.security.oauth2.provider.client.BaseClientDetails;
//
//import java.util.HashMap;
//import java.util.Map;
//
///**
// * 应用信息服务,表示一单个OAuth2应用。
// *
// * @author Sand
// * @version 1.0
// * @created 2016.03.04 9:51
// */
//public class EhrClientDetailsService implements ClientDetailsService {
//
//    private Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
//
////    @Autowired
//    private AppClient appClient;
//
//    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
//        ClientDetails details = clientDetailsStore.get(clientId);
//        if(details!=null){
//            return details;
//        }
//
//        MApp app = appClient.getApp(clientId);
//        if (app == null) {
//            throw new NoSuchClientException("Invalid client id: " + clientId);
//        }
//
//        BaseClientDetails baseClientDetails = new BaseClientDetails(
//                app.getId(),
//                "ehr",
//                "read,user,user.demographic_id,user.health_profiles,organization",
//                EhrTokenGranter.EhrAuthorizationCodeGranter.GRANT_TYPE,
//                "ROLE_CLIENT",
//                app.getUrl());
//
//        baseClientDetails.setClientSecret(app.getSecret());
//        clientDetailsStore.put(app.getId(), baseClientDetails);
//
//        return baseClientDetails;
//    }
//}

+ 297 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/inMemory/EhrInMemoryTokenStoreService.java

@ -0,0 +1,297 @@
package com.yihu.ehr.oauth2.oauth2.inMemory;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.Assert;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Token存储服务。Token的存储由svr-security服务实现,其实现分为JDBC存储与REDIS存储。Redis存储可加快Token访问速度。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.05 15:38
 */
public class EhrInMemoryTokenStoreService implements TokenStore {
    private static final int DEFAULT_FLUSH_INTERVAL = 1000;
    private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();
    private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();
    private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> userNameToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();
    private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> clientIdToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();
    private final ConcurrentHashMap<String, OAuth2RefreshToken> refreshTokenStore = new ConcurrentHashMap<String, OAuth2RefreshToken>();
    private final ConcurrentHashMap<String, String> accessTokenToRefreshTokenStore = new ConcurrentHashMap<String, String>();
    private final ConcurrentHashMap<String, OAuth2Authentication> authenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();
    private final ConcurrentHashMap<String, OAuth2Authentication> refreshTokenAuthenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();
    private final ConcurrentHashMap<String, String> refreshTokenToAccessTokenStore = new ConcurrentHashMap<String, String>();
    private final DelayQueue<TokenExpiry> expiryQueue = new DelayQueue<TokenExpiry>();
    private final ConcurrentHashMap<String, TokenExpiry> expiryMap = new ConcurrentHashMap<String, TokenExpiry>();
    private int flushInterval = DEFAULT_FLUSH_INTERVAL;
    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
    private AtomicInteger flushCounter = new AtomicInteger(0);
    /**
     * The number of tokens to store before flushing expired tokens. Defaults to 1000.
     *
     * @param flushInterval the interval to set
     */
    public void setFlushInterval(int flushInterval) {
        this.flushInterval = flushInterval;
    }
    /**
     * The interval (count of token inserts) between flushing expired tokens.
     *
     * @return the flushInterval the flush interval
     */
    public int getFlushInterval() {
        return flushInterval;
    }
    /**
     * Convenience method for super admin users to remove all tokens (useful for testing, not really in production)
     */
    public void clear() {
        accessTokenStore.clear();
        authenticationToAccessTokenStore.clear();
        clientIdToAccessTokenStore.clear();
        refreshTokenStore.clear();
        accessTokenToRefreshTokenStore.clear();
        authenticationStore.clear();
        refreshTokenAuthenticationStore.clear();
        refreshTokenToAccessTokenStore.clear();
        expiryQueue.clear();
    }
    public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
        this.authenticationKeyGenerator = authenticationKeyGenerator;
    }
    public int getAccessTokenCount() {
        Assert.state(accessTokenStore.isEmpty() || accessTokenStore.size() >= accessTokenToRefreshTokenStore.size(),
                "Too many refresh tokens");
        Assert.state(accessTokenStore.size() == authenticationToAccessTokenStore.size(),
                "Inconsistent token store state");
        Assert.state(accessTokenStore.size() <= authenticationStore.size(), "Inconsistent authentication store state");
        return accessTokenStore.size();
    }
    public int getRefreshTokenCount() {
        Assert.state(refreshTokenStore.size() == refreshTokenToAccessTokenStore.size(),
                "Inconsistent refresh token store state");
        return accessTokenStore.size();
    }
    public int getExpiryTokenCount() {
        return expiryQueue.size();
    }
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        String key = authenticationKeyGenerator.extractKey(authentication);
        OAuth2AccessToken accessToken = authenticationToAccessTokenStore.get(key);
        if (accessToken != null
                && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
            // Keep the stores consistent (maybe the same user is represented by this authentication but the details
            // have changed)
            storeAccessToken(accessToken, authentication);
        }
        return accessToken;
    }
    public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
        return readAuthentication(token.getValue());
    }
    public OAuth2Authentication readAuthentication(String token) {
        return this.authenticationStore.get(token);
    }
    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
        return readAuthenticationForRefreshToken(token.getValue());
    }
    public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
        return this.refreshTokenAuthenticationStore.get(token);
    }
    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        if (this.flushCounter.incrementAndGet() >= this.flushInterval) {
            flush();
            this.flushCounter.set(0);
        }
        this.accessTokenStore.put(token.getValue(), token);
        this.authenticationStore.put(token.getValue(), authentication);
        this.authenticationToAccessTokenStore.put(authenticationKeyGenerator.extractKey(authentication), token);
        if (!authentication.isClientOnly()) {
            addToCollection(this.userNameToAccessTokenStore, getApprovalKey(authentication), token);
        }
        addToCollection(this.clientIdToAccessTokenStore, authentication.getOAuth2Request().getClientId(), token);
        if (token.getExpiration() != null) {
            TokenExpiry expiry = new TokenExpiry(token.getValue(), token.getExpiration());
            // Remove existing expiry for this token if present
            expiryQueue.remove(expiryMap.put(token.getValue(), expiry));
            this.expiryQueue.put(expiry);
        }
        if (token.getRefreshToken() != null && token.getRefreshToken().getValue() != null) {
            this.refreshTokenToAccessTokenStore.put(token.getRefreshToken().getValue(), token.getValue());
            this.accessTokenToRefreshTokenStore.put(token.getValue(), token.getRefreshToken().getValue());
        }
    }
    private String getApprovalKey(OAuth2Authentication authentication) {
        String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication()
                .getName();
        return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
    }
    private String getApprovalKey(String clientId, String userName) {
        return clientId + (userName==null ? "" : ":" + userName);
    }
    private void addToCollection(ConcurrentHashMap<String, Collection<OAuth2AccessToken>> store, String key,
                                 OAuth2AccessToken token) {
        if (!store.containsKey(key)) {
            synchronized (store) {
                if (!store.containsKey(key)) {
                    store.put(key, new HashSet<OAuth2AccessToken>());
                }
            }
        }
        store.get(key).add(token);
    }
    public void removeAccessToken(OAuth2AccessToken accessToken) {
        removeAccessToken(accessToken.getValue());
    }
    public OAuth2AccessToken readAccessToken(String tokenValue) {
        return this.accessTokenStore.get(tokenValue);
    }
    public void removeAccessToken(String tokenValue) {
        OAuth2AccessToken removed = this.accessTokenStore.remove(tokenValue);
        this.accessTokenToRefreshTokenStore.remove(tokenValue);
        // Don't remove the refresh token - it's up to the caller to do that
        OAuth2Authentication authentication = this.authenticationStore.remove(tokenValue);
        if (authentication != null) {
            this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));
            Collection<OAuth2AccessToken> tokens;
            String clientId = authentication.getOAuth2Request().getClientId();
            tokens = this.userNameToAccessTokenStore.get(getApprovalKey(clientId, authentication.getName()));
            if (tokens != null) {
                tokens.remove(removed);
            }
            tokens = this.clientIdToAccessTokenStore.get(clientId);
            if (tokens != null) {
                tokens.remove(removed);
            }
            this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));
        }
    }
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        this.refreshTokenStore.put(refreshToken.getValue(), refreshToken);
        this.refreshTokenAuthenticationStore.put(refreshToken.getValue(), authentication);
    }
    public OAuth2RefreshToken readRefreshToken(String tokenValue) {
        return this.refreshTokenStore.get(tokenValue);
    }
    public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
        removeRefreshToken(refreshToken.getValue());
    }
    public void removeRefreshToken(String tokenValue) {
        this.refreshTokenStore.remove(tokenValue);
        this.refreshTokenAuthenticationStore.remove(tokenValue);
        this.refreshTokenToAccessTokenStore.remove(tokenValue);
    }
    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
        removeAccessTokenUsingRefreshToken(refreshToken.getValue());
    }
    private void removeAccessTokenUsingRefreshToken(String refreshToken) {
        String accessToken = this.refreshTokenToAccessTokenStore.remove(refreshToken);
        if (accessToken != null) {
            removeAccessToken(accessToken);
        }
    }
    public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
        Collection<OAuth2AccessToken> result = userNameToAccessTokenStore.get(getApprovalKey(clientId, userName));
        return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections
                .<OAuth2AccessToken> emptySet();
    }
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        Collection<OAuth2AccessToken> result = clientIdToAccessTokenStore.get(clientId);
        return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections
                .<OAuth2AccessToken> emptySet();
    }
    private void flush() {
        TokenExpiry expiry = expiryQueue.poll();
        while (expiry != null) {
            removeAccessToken(expiry.getValue());
            expiry = expiryQueue.poll();
        }
    }
    private static class TokenExpiry implements Delayed {
        private final long expiry;
        private final String value;
        public TokenExpiry(String value, Date date) {
            this.value = value;
            this.expiry = date.getTime();
        }
        public int compareTo(Delayed other) {
            if (this == other) {
                return 0;
            }
            long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);
            return (diff == 0 ? 0 : ((diff < 0) ? -1 : 1));
        }
        public long getDelay(TimeUnit unit) {
            return expiry - System.currentTimeMillis();
        }
        public String getValue() {
            return value;
        }
    }
}

+ 55 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcAccessUrlService.java

@ -0,0 +1,55 @@
package com.yihu.ehr.oauth2.oauth2.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.util.Map;
/**
 * Created by progr1mmer on 2017/9/19.
 */
public class EhrJdbcAccessUrlService {
    private JdbcTemplate jdbcTemplate;
    public EhrJdbcAccessUrlService(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    public String getValidUrl(String clientId, String redirectUriParameter) {
        String cleanUrl;
        if (redirectUriParameter.indexOf("?") != -1) {
            cleanUrl = redirectUriParameter.substring(0, redirectUriParameter.indexOf("?"));
        } else {
            cleanUrl = redirectUriParameter;
        }
        String sql = "SELECT url, out_url FROM apps WHERE id = '" + clientId + "'";
        Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql);
        if (cleanUrl.equals(resultMap.get("url").toString()) || cleanUrl.equals(resultMap.get("out_url").toString())) {
            return resultMap.get("url").toString();
        } else {
            return redirectUriParameter;
        }
    }
    public String getRealUrl(String clientId, String redirectUriParameter) {
        String cleanUrl;
        String queryParam;
        if (redirectUriParameter.indexOf("?") != -1) {
            queryParam = "&clientId=" + clientId;
            cleanUrl = redirectUriParameter.substring(0, redirectUriParameter.indexOf("?"));
        } else {
            queryParam = "?clientId=" + clientId;
            cleanUrl = redirectUriParameter;
        }
        String sql = "SELECT url, out_url FROM apps WHERE id = '" + clientId + "'";
        Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql);
        if (cleanUrl.equals(resultMap.get("url").toString()) || cleanUrl.equals(resultMap.get("out_url").toString())) {
            return redirectUriParameter + queryParam;
        } else {
            return resultMap.get("url").toString() + queryParam;
        }
    }
}

+ 82 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcAuthorizationCodeService.java

@ -0,0 +1,82 @@
package com.yihu.ehr.oauth2.oauth2.jdbc;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.SqlLobValue;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Date;
/**
 * OAuth2验证码服务 jdbc 版本
 * code 的服务类
 * @author cwd
 * @version 1.0
 * @created 2017.02.21 11:03
 */
/** new version 使用内存存储 */
public class EhrJdbcAuthorizationCodeService extends RandomValueAuthorizationCodeServices {
    private static final String DEFAULT_SELECT_STATEMENT = "select code, authentication from oauth_code where code = ?";
    private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_code (code, authentication, create_time) values (?, ?, ?)";
    private static final String DEFAULT_DELETE_STATEMENT = "delete from oauth_code where code = ?";
    private String selectAuthenticationSql = DEFAULT_SELECT_STATEMENT;
    private String insertAuthenticationSql = DEFAULT_INSERT_STATEMENT;
    private String deleteAuthenticationSql = DEFAULT_DELETE_STATEMENT;
    private final JdbcTemplate jdbcTemplate;
    public EhrJdbcAuthorizationCodeService(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    @Override
    protected void store(String code, OAuth2Authentication authentication) {
        jdbcTemplate.update(insertAuthenticationSql,
                new Object[]{code, new SqlLobValue(SerializationUtils.serialize(authentication)), new Date()}, new int[]{
                        Types.VARCHAR, Types.BLOB, Types.TIMESTAMP});
    }
    public OAuth2Authentication remove(String code) {
        OAuth2Authentication authentication;
        try {
            authentication = jdbcTemplate.queryForObject(selectAuthenticationSql,
                    new RowMapper<OAuth2Authentication>() {
                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum)
                                throws SQLException {
                            return SerializationUtils.deserialize(rs.getBytes("authentication"));
                        }
                    }, code);
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
        if (authentication != null) {
            jdbcTemplate.update(deleteAuthenticationSql, code);
        }
        return authentication;
    }
    public void setSelectAuthenticationSql(String selectAuthenticationSql) {
        this.selectAuthenticationSql = selectAuthenticationSql;
    }
    public void setInsertAuthenticationSql(String insertAuthenticationSql) {
        this.insertAuthenticationSql = insertAuthenticationSql;
    }
    public void setDeleteAuthenticationSql(String deleteAuthenticationSql) {
        this.deleteAuthenticationSql = deleteAuthenticationSql;
    }
}

+ 43 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcClientDetailsService.java

@ -0,0 +1,43 @@
package com.yihu.ehr.oauth2.oauth2.jdbc;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import javax.sql.DataSource;
/**
 * 应用信息服务,表示一单个OAuth2应用。
 * 修改成JdbcClientDetailsService
 * @author cwd
 * @version 1.0
 * @created 2017.02.21 11:03
 */
public class EhrJdbcClientDetailsService extends JdbcClientDetailsService {
    public EhrJdbcClientDetailsService(DataSource dataSource) {
        super(dataSource);
    }
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        return super.loadClientByClientId(clientId);
    }
    @Override
    public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
        super.updateClientDetails(clientDetails);
    }
    @Override
    public void updateClientSecret(String clientId, String secret) throws NoSuchClientException {
        super.updateClientSecret(clientId, secret);
    }
    @Override
    public void removeClientDetails(String clientId) throws NoSuchClientException {
        super.removeClientDetails(clientId);
    }
}

+ 459 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcTokenStore.java

@ -0,0 +1,459 @@
package com.yihu.ehr.oauth2.oauth2.jdbc;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.SqlLobValue;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
 * Token存储服务。Token的存储由svr-security服务实现,其实现分为JDBC存储与REDIS存储。Redis存储可加快Token访问速度。
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.05 15:38
 */
public class EhrJdbcTokenStore implements TokenStore {
    private static final Log LOG = LogFactory.getLog(EhrJdbcTokenStore.class);
    private static final String DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT = "insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token,create_time) values (?, ?, ?, ?, ?, ?, ?,now())";
    private static final String DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id = ?";
    private static final String DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_access_token where token_id = ?";
    private static final String DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT = "select token_id, token from oauth_access_token where authentication_id = ?";
    private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ? and client_id = ?";
    private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ?";
    private static final String DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT = "select token_id, token from oauth_access_token where client_id = ?";
    private static final String DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT = "delete from oauth_access_token where token_id = ?";
    private static final String DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT = "delete from oauth_access_token where refresh_token = ?";
    private static final String DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT = "insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)";
    private static final String DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_refresh_token where token_id = ?";
    private static final String DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_refresh_token where token_id = ?";
    private static final String DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT = "delete from oauth_refresh_token where token_id = ?";
    private String insertAccessTokenSql = DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT;
    private String selectAccessTokenSql = DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT;
    private String selectAccessTokenAuthenticationSql = DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
    private String selectAccessTokenFromAuthenticationSql = DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT;
    private String selectAccessTokensFromUserNameAndClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT;
    private String selectAccessTokensFromUserNameSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT;
    private String selectAccessTokensFromClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT;
    private String deleteAccessTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT;
    private String insertRefreshTokenSql = DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT;
    private String selectRefreshTokenSql = DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT;
    private String selectRefreshTokenAuthenticationSql = DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
    private String deleteRefreshTokenSql = DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT;
    private String deleteAccessTokenFromRefreshTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT;
    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
    private final JdbcTemplate jdbcTemplate;
    public EhrJdbcTokenStore(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
        this.authenticationKeyGenerator = authenticationKeyGenerator;
    }
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        OAuth2AccessToken accessToken = null;
        String key = authenticationKeyGenerator.extractKey(authentication);
        try {
            accessToken = jdbcTemplate.queryForObject(selectAccessTokenFromAuthenticationSql,
                    new RowMapper<OAuth2AccessToken>() {
                        public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return deserializeAccessToken(rs.getBytes(2));
                        }
                    }, key);
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Failed to find access token for authentication " + authentication);
            }
        }
        catch (IllegalArgumentException e) {
            LOG.error("Could not extract access token for authentication " + authentication, e);
        }
        if (accessToken != null
                && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
            removeAccessToken(accessToken.getValue());
            // Keep the store consistent (maybe the same user is represented by this authentication but the details have
            // changed)
            storeAccessToken(accessToken, authentication);
        }
        return accessToken;
    }
    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        String refreshToken = null;
        if (token.getRefreshToken() != null) {
            refreshToken = token.getRefreshToken().getValue();
        }
        if (readAccessToken(token.getValue())!=null) {
            removeAccessToken(token.getValue());
        }
        jdbcTemplate.update(insertAccessTokenSql, new Object[] { extractTokenKey(token.getValue()),
                new SqlLobValue(serializeAccessToken(token)), authenticationKeyGenerator.extractKey(authentication),
                authentication.isClientOnly() ? null : authentication.getName(),
                authentication.getOAuth2Request().getClientId(),
                new SqlLobValue(serializeAuthentication(authentication)), extractTokenKey(refreshToken) }, new int[] {
                Types.VARCHAR, Types.BLOB, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BLOB, Types.VARCHAR });
    }
    public OAuth2AccessToken readAccessToken(String tokenValue) {
        OAuth2AccessToken accessToken = null;
        try {
            accessToken = jdbcTemplate.queryForObject(selectAccessTokenSql, new RowMapper<OAuth2AccessToken>() {
                public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return deserializeAccessToken(rs.getBytes(2));
                }
            }, extractTokenKey(tokenValue));
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find access token for token " + tokenValue);
            }
        }
        catch (IllegalArgumentException e) {
            LOG.warn("Failed to deserialize access token for " + tokenValue, e);
            removeAccessToken(tokenValue);
        }
        return accessToken;
    }
    public void removeAccessToken(OAuth2AccessToken token) {
        removeAccessToken(token.getValue());
    }
    public void removeAccessToken(String tokenValue) {
        jdbcTemplate.update(deleteAccessTokenSql, extractTokenKey(tokenValue));
    }
    public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
        return readAuthentication(token.getValue());
    }
    public OAuth2Authentication readAuthentication(String token) {
        OAuth2Authentication authentication = null;
        try {
            authentication = jdbcTemplate.queryForObject(selectAccessTokenAuthenticationSql,
                    new RowMapper<OAuth2Authentication>() {
                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return deserializeAuthentication(rs.getBytes(2));
                        }
                    }, extractTokenKey(token));
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find access token for token " + token);
            }
        }
        catch (IllegalArgumentException e) {
            LOG.warn("Failed to deserialize authentication for " + token, e);
            removeAccessToken(token);
        }
        return authentication;
    }
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        jdbcTemplate.update(insertRefreshTokenSql, new Object[] { extractTokenKey(refreshToken.getValue()),
                new SqlLobValue(serializeRefreshToken(refreshToken)),
                new SqlLobValue(serializeAuthentication(authentication)) }, new int[] { Types.VARCHAR, Types.BLOB,
                Types.BLOB });
    }
    public OAuth2RefreshToken readRefreshToken(String token) {
        OAuth2RefreshToken refreshToken = null;
        try {
            refreshToken = jdbcTemplate.queryForObject(selectRefreshTokenSql, new RowMapper<OAuth2RefreshToken>() {
                public OAuth2RefreshToken mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return deserializeRefreshToken(rs.getBytes(2));
                }
            }, extractTokenKey(token));
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find refresh token for token " + token);
            }
        }
        catch (IllegalArgumentException e) {
            LOG.warn("Failed to deserialize refresh token for token " + token, e);
            removeRefreshToken(token);
        }
        return refreshToken;
    }
    public void removeRefreshToken(OAuth2RefreshToken token) {
        removeRefreshToken(token.getValue());
    }
    public void removeRefreshToken(String token) {
        jdbcTemplate.update(deleteRefreshTokenSql, extractTokenKey(token));
    }
    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
        return readAuthenticationForRefreshToken(token.getValue());
    }
    public OAuth2Authentication readAuthenticationForRefreshToken(String value) {
        OAuth2Authentication authentication = null;
        try {
            authentication = jdbcTemplate.queryForObject(selectRefreshTokenAuthenticationSql,
                    new RowMapper<OAuth2Authentication>() {
                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
                            return deserializeAuthentication(rs.getBytes(2));
                        }
                    }, extractTokenKey(value));
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find access token for token " + value);
            }
        }
        catch (IllegalArgumentException e) {
            LOG.warn("Failed to deserialize access token for " + value, e);
            removeRefreshToken(value);
        }
        return authentication;
    }
    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
        removeAccessTokenUsingRefreshToken(refreshToken.getValue());
    }
    public void removeAccessTokenUsingRefreshToken(String refreshToken) {
        jdbcTemplate.update(deleteAccessTokenFromRefreshTokenSql, new Object[] { extractTokenKey(refreshToken) },
                new int[] { Types.VARCHAR });
    }
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>();
        try {
            accessTokens = jdbcTemplate.query(selectAccessTokensFromClientIdSql, new SafeAccessTokenRowMapper(),
                    clientId);
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find access token for clientId " + clientId);
            }
        }
        accessTokens = removeNulls(accessTokens);
        return accessTokens;
    }
    public Collection<OAuth2AccessToken> findTokensByUserName(String userName) {
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>();
        try {
            accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameSql, new SafeAccessTokenRowMapper(),
                    userName);
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled())
                LOG.info("Failed to find access token for userName " + userName);
        }
        accessTokens = removeNulls(accessTokens);
        return accessTokens;
    }
    public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>();
        try {
            accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameAndClientIdSql, new SafeAccessTokenRowMapper(),
                    userName, clientId);
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.info("Failed to find access token for clientId " + clientId + " and userName " + userName);
            }
        }
        accessTokens = removeNulls(accessTokens);
        return accessTokens;
    }
    private List<OAuth2AccessToken> removeNulls(List<OAuth2AccessToken> accessTokens) {
        List<OAuth2AccessToken> tokens = new ArrayList<OAuth2AccessToken>();
        for (OAuth2AccessToken token : accessTokens) {
            if (token != null) {
                tokens.add(token);
            }
        }
        return tokens;
    }
    protected String extractTokenKey(String value) {
        if (value == null) {
            return null;
        }
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
        }
        try {
            byte[] bytes = digest.digest(value.getBytes("UTF-8"));
            return String.format("%032x", new BigInteger(1, bytes));
        }
        catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
        }
    }
    private final class SafeAccessTokenRowMapper implements RowMapper<OAuth2AccessToken> {
        public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
            try {
                return deserializeAccessToken(rs.getBytes(2));
            }
            catch (IllegalArgumentException e) {
                String token = rs.getString(1);
                jdbcTemplate.update(deleteAccessTokenSql, token);
                return null;
            }
        }
    }
    protected byte[] serializeAccessToken(OAuth2AccessToken token) {
        return SerializationUtils.serialize(token);
    }
    protected byte[] serializeRefreshToken(OAuth2RefreshToken token) {
        return SerializationUtils.serialize(token);
    }
    protected byte[] serializeAuthentication(OAuth2Authentication authentication) {
        return SerializationUtils.serialize(authentication);
    }
    protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
        return SerializationUtils.deserialize(token);
    }
    protected OAuth2RefreshToken deserializeRefreshToken(byte[] token) {
        return SerializationUtils.deserialize(token);
    }
    protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
        return SerializationUtils.deserialize(authentication);
    }
    public void setInsertAccessTokenSql(String insertAccessTokenSql) {
        this.insertAccessTokenSql = insertAccessTokenSql;
    }
    public void setSelectAccessTokenSql(String selectAccessTokenSql) {
        this.selectAccessTokenSql = selectAccessTokenSql;
    }
    public void setDeleteAccessTokenSql(String deleteAccessTokenSql) {
        this.deleteAccessTokenSql = deleteAccessTokenSql;
    }
    public void setInsertRefreshTokenSql(String insertRefreshTokenSql) {
        this.insertRefreshTokenSql = insertRefreshTokenSql;
    }
    public void setSelectRefreshTokenSql(String selectRefreshTokenSql) {
        this.selectRefreshTokenSql = selectRefreshTokenSql;
    }
    public void setDeleteRefreshTokenSql(String deleteRefreshTokenSql) {
        this.deleteRefreshTokenSql = deleteRefreshTokenSql;
    }
    public void setSelectAccessTokenAuthenticationSql(String selectAccessTokenAuthenticationSql) {
        this.selectAccessTokenAuthenticationSql = selectAccessTokenAuthenticationSql;
    }
    public void setSelectRefreshTokenAuthenticationSql(String selectRefreshTokenAuthenticationSql) {
        this.selectRefreshTokenAuthenticationSql = selectRefreshTokenAuthenticationSql;
    }
    public void setSelectAccessTokenFromAuthenticationSql(String selectAccessTokenFromAuthenticationSql) {
        this.selectAccessTokenFromAuthenticationSql = selectAccessTokenFromAuthenticationSql;
    }
    public void setDeleteAccessTokenFromRefreshTokenSql(String deleteAccessTokenFromRefreshTokenSql) {
        this.deleteAccessTokenFromRefreshTokenSql = deleteAccessTokenFromRefreshTokenSql;
    }
    public void setSelectAccessTokensFromUserNameSql(String selectAccessTokensFromUserNameSql) {
        this.selectAccessTokensFromUserNameSql = selectAccessTokensFromUserNameSql;
    }
    public void setSelectAccessTokensFromUserNameAndClientIdSql(String selectAccessTokensFromUserNameAndClientIdSql) {
        this.selectAccessTokensFromUserNameAndClientIdSql = selectAccessTokensFromUserNameAndClientIdSql;
    }
    public void setSelectAccessTokensFromClientIdSql(String selectAccessTokensFromClientIdSql) {
        this.selectAccessTokensFromClientIdSql = selectAccessTokensFromClientIdSql;
    }
}

+ 49 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/jdbc/EhrJdbcUserSecurityService.java

@ -0,0 +1,49 @@
package com.yihu.ehr.oauth2.oauth2.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.util.List;
/**
 * Created by progr1mmer on 2018/1/8.
 */
public class EhrJdbcUserSecurityService {
    private static final String DEFAULT_KEY_ID_SELECT_STATEMENT = "SELECT id FROM user_security WHERE public_key = ?";
    private static final String DEFAULT_USER_ID_BY_KEY_ID_SELECT_STATEMENT = "SELECT user_id FROM user_key WHERE key_id = ?";
    private static final String DEFAULT_USER_NAME_BY_USER_ID = "SELECT login_code FROM users WHERE id = ?";
    private JdbcTemplate jdbcTemplate;
    public EhrJdbcUserSecurityService(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    public String getDefaultKeyIdSelectStatement(String publicKey) {
        List<String> keyId = jdbcTemplate.queryForList(DEFAULT_KEY_ID_SELECT_STATEMENT, new String []{publicKey}, String.class);
        if(keyId.size() <= 0) {
            throw new InsufficientAuthenticationException("Illegal authorized pk.");
        }
        return keyId.get(0);
    }
    public String getDefaultUserIdByKeyIdSelectStatement(String keyId) {
        List<String> userId =  jdbcTemplate.queryForList(DEFAULT_USER_ID_BY_KEY_ID_SELECT_STATEMENT, new String []{keyId}, String.class);
        if(userId.size() <= 0) {
            throw new InsufficientAuthenticationException("Unauthorized security key. please check you pk.");
        }
        return userId.get(0);
    }
    public String getDefaultUserNameByUserId(String userId) {
        List<String> username = jdbcTemplate.queryForList(DEFAULT_USER_NAME_BY_USER_ID, new String []{userId}, String.class);
        if(username.size() <= 0) {
            throw new InsufficientAuthenticationException("Illegal user id. please check you pk.");
        }
        return username.get(0);
    }
}

+ 80 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisApiAccessValidator.java

@ -0,0 +1,80 @@
package com.yihu.ehr.oauth2.oauth2.redis;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 *
 * Created by progr1mmer on 2018/1/10.
 */
@Component
public class EhrRedisApiAccessValidator {
    //private static final String DEFAULT_USER_ID_BY_USER_NAME_STATEMENT = "SELECT id FROM users WHERE login_code = ?";
    private static final String DEFAULT_APP_API_STATEMENT = "SELECT CONCAT(aa.micro_service_url, aa.ms_method_name) FROM apps_api aa \n" +
            "\tLEFT JOIN role_api_relation rar ON rar.app_api_id = aa.id \n" +
            "\tWHERE rar.role_id IN (SELECT role_id FROM role_app_relation WHERE app_id = ?) \n" +
            "\tAND LENGTH(aa.micro_service_url) > 0 AND LENGTH(aa.ms_method_name) > 0";
    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void putVerificationApi (String clientId, String userName) {
        /**
        List<String> userId = jdbcTemplate.queryForList(DEFAULT_USER_ID_BY_USER_NAME_STATEMENT, new String []{userName}, String.class);
        if(userId.size() <= 0) {
            throw new InsufficientAuthenticationException("Illegal authorized user.");
        }
         */
        List<String> appApiList = jdbcTemplate.queryForList(DEFAULT_APP_API_STATEMENT, new String[]{clientId}, String.class);
        if(appApiList.size() > 0) {
            Date today = new Date();
            Date tomorrow = DateUtils.addDays(today, 1);
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            try {
                tomorrow = dateFormat.parse(dateFormat.format(tomorrow));
            } catch (ParseException e) {
            }
            long expire = tomorrow.getTime() - today.getTime();
            for (String api : appApiList) {
                String key = clientId + "$" + userName + "$" + api;
                if (null == redisTemplate.opsForValue().get(key)) {
                    redisTemplate.opsForValue().set(key, 200);
                    redisTemplate.expire(key, expire, TimeUnit.MILLISECONDS);
                }
            }
        }
    }
    public boolean verificationApi(String clientId, String userName, String api) {
        String key = clientId + "$" + userName + "$" + api;
        Serializable serializable = redisTemplate.opsForValue().get(key);
        if(serializable != null) {
            int count = new Integer(serializable.toString());
            if (count > 0 ) {
                long expire = redisTemplate.getExpire(key);
                redisTemplate.opsForValue().set(key, --count);
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
                return true;
            } else {
                return false;
            }
        }
        return false;
    }
}

+ 395 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisTokenStore.java

@ -0,0 +1,395 @@
package com.yihu.ehr.oauth2.oauth2.redis;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
/**
 * @author Progr1mmer
 */
public class EhrRedisTokenStore implements TokenStore {
    private static final String ACCESS = "access:";
    private static final String AUTH_TO_ACCESS = "auth_to_access:";
    private static final String AUTH = "auth:";
    private static final String REFRESH_AUTH = "refresh_auth:";
    private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
    private static final String REFRESH = "refresh:";
    private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
    private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
    private static final String UNAME_TO_ACCESS = "uname_to_access:";
    private final RedisConnectionFactory connectionFactory;
    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
    private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
    private String prefix = "";
    public EhrRedisTokenStore(RedisConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }
    public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
        this.authenticationKeyGenerator = authenticationKeyGenerator;
    }
    public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
        this.serializationStrategy = serializationStrategy;
    }
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    private RedisConnection getConnection() {
        return connectionFactory.getConnection();
    }
    private byte[] serialize(Object object) {
        return serializationStrategy.serialize(object);
    }
    private byte[] serializeKey(String object) {
        return serialize(prefix + object);
    }
    private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
        return serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
    }
    private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
        return serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
    }
    private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
        return serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
    }
    private byte[] serialize(String string) {
        return serializationStrategy.serialize(string);
    }
    private String deserializeString(byte[] bytes) {
        return serializationStrategy.deserializeString(bytes);
    }
    @Override
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        String key = authenticationKeyGenerator.extractKey(authentication);
        byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key);
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(serializedKey);
        } finally {
            conn.close();
        }
        OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
        if (accessToken != null) {
            OAuth2Authentication oAuth2Authentication = readAuthentication(accessToken.getValue());
            if (oAuth2Authentication == null || !key.equals(authenticationKeyGenerator.extractKey(authentication))) {
                // Keep the stores consistent (maybe the same user is
                // represented by this authentication but the details have
                // changed)
                storeAccessToken(accessToken, authentication);
            }
        }
        return accessToken;
    }
    @Override
    public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
        return readAuthentication(token.getValue());
    }
    @Override
    public OAuth2Authentication readAuthentication(String token) {
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(serializeKey(AUTH + token));
        } finally {
            conn.close();
        }
        OAuth2Authentication auth = deserializeAuthentication(bytes);
        return auth;
    }
    @Override
    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
        return readAuthenticationForRefreshToken(token.getValue());
    }
    public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
        RedisConnection conn = getConnection();
        try {
            byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
            OAuth2Authentication auth = deserializeAuthentication(bytes);
            return auth;
        } finally {
            conn.close();
        }
    }
    @Override
    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        byte[] serializedAccessToken = serialize(token);
        byte[] serializedAuth = serialize(authentication);
        byte[] accessKey = serializeKey(ACCESS + token.getValue());
        byte[] authKey = serializeKey(AUTH + token.getValue());
        byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
        byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
        byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
        RedisConnection conn = getConnection();
        try {
            conn.openPipeline();
            conn.set(accessKey, serializedAccessToken);
            conn.set(authKey, serializedAuth);
            conn.set(authToAccessKey, serializedAccessToken);
            if (!authentication.isClientOnly()) {
                conn.rPush(approvalKey, serializedAccessToken);
            }
            conn.rPush(clientId, serializedAccessToken);
            if (token.getExpiration() != null) {
                int seconds = token.getExpiresIn();
                conn.expire(accessKey, seconds);
                conn.expire(authKey, seconds);
                conn.expire(authToAccessKey, seconds);
                conn.expire(clientId, seconds);
                conn.expire(approvalKey, seconds);
            }
            OAuth2RefreshToken refreshToken = token.getRefreshToken();
            if (refreshToken != null && refreshToken.getValue() != null) {
                byte[] refresh = serialize(token.getRefreshToken().getValue());
                byte[] auth = serialize(token.getValue());
                byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
                conn.set(refreshToAccessKey, auth);
                byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue());
                conn.set(accessToRefreshKey, refresh);
                if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
                    ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
                    Date expiration = expiringRefreshToken.getExpiration();
                    if (expiration != null) {
                        int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
                                .intValue();
                        conn.expire(refreshToAccessKey, seconds);
                        conn.expire(accessToRefreshKey, seconds);
                    }
                }
            }
            conn.closePipeline();
        } finally {
            conn.close();
        }
    }
    private static String getApprovalKey(OAuth2Authentication authentication) {
        String userName = authentication.getUserAuthentication() == null ? ""
                : authentication.getUserAuthentication().getName();
        return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
    }
    private static String getApprovalKey(String clientId, String userName) {
        return clientId + (userName == null ? "" : ":" + userName);
    }
    @Override
    public void removeAccessToken(OAuth2AccessToken accessToken) {
        removeAccessToken(accessToken.getValue());
    }
    @Override
    public OAuth2AccessToken readAccessToken(String tokenValue) {
        byte[] key = serializeKey(ACCESS + tokenValue);
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(key);
        } finally {
            conn.close();
        }
        OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
        return accessToken;
    }
    public void removeAccessToken(String tokenValue) {
        byte[] accessKey = serializeKey(ACCESS + tokenValue);
        byte[] authKey = serializeKey(AUTH + tokenValue);
        byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
        RedisConnection conn = getConnection();
        try {
            conn.openPipeline();
            conn.get(accessKey);
            conn.get(authKey);
            conn.del(accessKey);
            conn.del(accessToRefreshKey);
            // Don't remove the refresh token - it's up to the caller to do that
            conn.del(authKey);
            List<Object> results = conn.closePipeline();
            byte[] access = (byte[]) results.get(0);
            byte[] auth = (byte[]) results.get(1);
            OAuth2Authentication authentication = deserializeAuthentication(auth);
            if (authentication != null) {
                String key = authenticationKeyGenerator.extractKey(authentication);
                byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key);
                byte[] unameKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
                byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
                conn.openPipeline();
                conn.del(authToAccessKey);
                conn.lRem(unameKey, 1, access);
                conn.lRem(clientId, 1, access);
                conn.del(serialize(ACCESS + key));
                conn.closePipeline();
            }
        } finally {
            conn.close();
        }
    }
    @Override
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue());
        byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue());
        byte[] serializedRefreshToken = serialize(refreshToken);
        RedisConnection conn = getConnection();
        try {
            conn.openPipeline();
            conn.set(refreshKey, serializedRefreshToken);
            conn.set(refreshAuthKey, serialize(authentication));
            if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
                ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
                Date expiration = expiringRefreshToken.getExpiration();
                if (expiration != null) {
                    int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
                            .intValue();
                    conn.expire(refreshKey, seconds);
                    conn.expire(refreshAuthKey, seconds);
                }
            }
            conn.closePipeline();
        } finally {
            conn.close();
        }
    }
    @Override
    public OAuth2RefreshToken readRefreshToken(String tokenValue) {
        byte[] key = serializeKey(REFRESH + tokenValue);
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(key);
        } finally {
            conn.close();
        }
        OAuth2RefreshToken refreshToken = deserializeRefreshToken(bytes);
        return refreshToken;
    }
    @Override
    public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
        removeRefreshToken(refreshToken.getValue());
    }
    public void removeRefreshToken(String tokenValue) {
        byte[] refreshKey = serializeKey(REFRESH + tokenValue);
        byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue);
        byte[] access2RefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
        RedisConnection conn = getConnection();
        try {
            conn.openPipeline();
            conn.del(refreshKey);
            conn.del(refresh2AccessKey);
            conn.del(access2RefreshKey);
            conn.closePipeline();
        } finally {
            conn.close();
        }
    }
    @Override
    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
        removeAccessTokenUsingRefreshToken(refreshToken.getValue());
    }
    private void removeAccessTokenUsingRefreshToken(String refreshToken) {
        byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken);
        List<Object> results = null;
        RedisConnection conn = getConnection();
        try {
            conn.openPipeline();
            conn.get(key);
            conn.del(key);
            results = conn.closePipeline();
        } finally {
            conn.close();
        }
        if (results == null) {
            return;
        }
        byte[] bytes = (byte[]) results.get(0);
        String accessToken = deserializeString(bytes);
        if (accessToken != null) {
            removeAccessToken(accessToken);
        }
    }
    @Override
    public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
        byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName));
        List<byte[]> byteList = null;
        RedisConnection conn = getConnection();
        try {
            byteList = conn.lRange(approvalKey, 0, -1);
        } finally {
            conn.close();
        }
        if (byteList == null || byteList.size() == 0) {
            return Collections.<OAuth2AccessToken> emptySet();
        }
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>(byteList.size());
        for (byte[] bytes : byteList) {
            OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
            accessTokens.add(accessToken);
        }
        return Collections.<OAuth2AccessToken> unmodifiableCollection(accessTokens);
    }
    @Override
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId);
        List<byte[]> byteList = null;
        RedisConnection conn = getConnection();
        try {
            byteList = conn.lRange(key, 0, -1);
        } finally {
            conn.close();
        }
        if (byteList == null || byteList.size() == 0) {
            return Collections.<OAuth2AccessToken> emptySet();
        }
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>(byteList.size());
        for (byte[] bytes : byteList) {
            OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
            accessTokens.add(accessToken);
        }
        return Collections.<OAuth2AccessToken> unmodifiableCollection(accessTokens);
    }
}

+ 46 - 0
src/main/java/com/yihu/ehr/oauth2/oauth2/redis/EhrRedisVerifyCodeService.java

@ -0,0 +1,46 @@
package com.yihu.ehr.oauth2.oauth2.redis;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
 * Created by progr1mmer on 2018/4/18.
 */
public class EhrRedisVerifyCodeService {
    private final String KEY_SUFFIX = ":code";
    private final RedisTemplate<String, Serializable> redisTemplate;
    public EhrRedisVerifyCodeService(RedisTemplate<String, Serializable> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    public void store (String client_id, String username, String code, long expire) {
        String key = client_id + ":" + username + KEY_SUFFIX;
        redisTemplate.opsForValue().set(key, code);
        redisTemplate.expire(key, expire, TimeUnit.MILLISECONDS);
    }
    public Integer getExpireTime (String client_id, String username) {
        return new Long(redisTemplate.getExpire(client_id + ":" + username + KEY_SUFFIX)).intValue();
    }
    public boolean verification (String client_id, String username, String code) {
        if (StringUtils.isEmpty(code)) {
            return false;
        }
        Serializable serializable = redisTemplate.opsForValue().get(client_id + ":" + username + KEY_SUFFIX);
        if (null == serializable) {
            return false;
        }
        String _code = serializable.toString();
        if (code.equalsIgnoreCase(_code)) {
            return true;
        }
        return false;
    }
}

+ 21 - 0
src/main/java/com/yihu/ehr/oauth2/utils/BasicAuthorizationExtractor.java

@ -0,0 +1,21 @@
package com.yihu.ehr.oauth2.utils;
import javafx.util.Pair;
import java.security.InvalidParameterException;
import java.util.Base64;
/**
 * Created by Sand Wen on 2016.3.9.
 */
public class BasicAuthorizationExtractor {
    public static Pair<String, String> extract(String authorization){
        if(null == authorization) throw new InvalidParameterException("Need basic authentication.");
        String base64 = authorization.substring(authorization.indexOf(' ') + 1);
        String compose[] = new String(Base64.getDecoder().decode(base64)).split(":");
        return new Pair<>(compose[0], compose.length > 1 ? compose[1] : "");
    }
}

+ 275 - 0
src/main/java/com/yihu/ehr/oauth2/web/EhrAccessTokenEndpoint.java

@ -0,0 +1,275 @@
/*
 * Copyright (c) 2015 MONKEYK Information Technology Co. Ltd
 * www.monkeyk.com
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * MONKEYK Information Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with MONKEYK Information Technology Co. Ltd.
 */
package com.yihu.ehr.oauth2.web;
import com.yihu.ehr.constants.ServiceApi;
import com.yihu.ehr.oauth2.model.AccessToken;
import com.yihu.ehr.oauth2.oauth2.EhrOAuth2ExceptionTranslator;
import com.yihu.ehr.oauth2.oauth2.EhrTokenGranter;
import com.yihu.ehr.oauth2.oauth2.EhrTokenServices;
import com.yihu.ehr.oauth2.oauth2.jdbc.EhrJdbcClientDetailsService;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisApiAccessValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
 * Ehr Oauth2 Server
 * AccessToken Endpoint
 * Created by Progr1mmer on 2018/01/09.
 */
@RestController
public class EhrAccessTokenEndpoint extends AbstractEndpoint {
    private static final Logger LOG = LoggerFactory.getLogger(EhrAccessTokenEndpoint.class);
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private EhrJdbcClientDetailsService ehrJdbcClientDetailsService;
    @Autowired
    private EhrTokenServices ehrTokenServices;
    @Autowired
    private EhrTokenGranter ehrTokenGranter;
    @Autowired
    private EhrRedisApiAccessValidator ehrRedisApiAccessValidator;
    @Autowired
    private EhrOAuth2ExceptionTranslator ehrOAuth2ExceptionTranslator;
    private OAuth2RequestFactory oAuth2RequestFactory;
    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
    @PostConstruct
    private void init() {
        super.setTokenGranter(ehrTokenGranter);
    }
    @RequestMapping(value = ServiceApi.Authentication.AccessToken, method = RequestMethod.POST)
    public ResponseEntity<AccessToken> accessToken(@RequestParam Map<String, String> parameters) {
        String grant_type = parameters.get("grant_type");
        String client_id = parameters.get("client_id");
        String scope = parameters.get("scope");
        //检查基本参数
        if (StringUtils.isEmpty(client_id)) {
            throw new InvalidRequestException("Missing client id");
        }
        if (StringUtils.isEmpty(grant_type)) {
            throw new InvalidRequestException("Missing grant type");
        }
        Map<String, String> param = new HashMap<>();
        param.put("grant_type", grant_type);
        param.put("client_id", client_id);
        param.put("scope", scope);
        if (grant_type.equals("authorization_code")) {
            param.put("code", parameters.get("code"));
            param.put("redirect_uri", parameters.get("redirect_uri"));
        } else if (grant_type.equals("password")) {
            param.put("username", parameters.get("username"));
            param.put("password", parameters.get("password"));
        } else if (grant_type.equals("refresh_token")){
            param.put("refresh_token", parameters.get("refresh_token"));
        } else {
            throw new UnsupportedGrantTypeException("unsupported grant type: " + grant_type);
        }
        ClientDetails authenticatedClient = ehrJdbcClientDetailsService.loadClientByClientId(client_id);
        TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(param, authenticatedClient);
        //校验 client_id 是否一致
        if (!client_id.equals(tokenRequest.getClientId())) {
            throw new InvalidClientException("Given client ID does not match authenticated client");
        }
        if (authenticatedClient != null) {
            oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
        }
        if (!StringUtils.hasText(tokenRequest.getGrantType())) {
            throw new InvalidRequestException("Missing grant type");
        }
        if (tokenRequest.getGrantType().equals("implicit")) {
            throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
        }
        if (isAuthCodeRequest(parameters)) {
            // The scope was requested or determined during the authorization step
            if (!tokenRequest.getScope().isEmpty()) {
                LOG.debug("Clearing scope of incoming token request");
                tokenRequest.setScope(Collections.<String>emptySet());
            }
        }
        if (isRefreshTokenRequest(parameters)) {
            // A refresh token has its own default scopes, so we should ignore any added by the factory here.
            tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
        }
        OAuth2AccessToken oAuth2AccessToken = getTokenGranter().grant(grant_type, tokenRequest);
        if (oAuth2AccessToken == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + grant_type);
        } else {
            AccessToken accessToken = new AccessToken();
            OAuth2Authentication authentication = ehrTokenServices.loadAuthentication(oAuth2AccessToken.getValue());
            accessToken.setAccessToken(oAuth2AccessToken.getValue());
            accessToken.setTokenType(oAuth2AccessToken.getTokenType());
            accessToken.setExpiresIn(oAuth2AccessToken.getExpiresIn());
            accessToken.setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
            accessToken.setUser(authentication.getName());
            accessToken.setState(parameters.get("state"));
            putVerificationApi(tokenRequest, authentication);
            return getResponse(accessToken);
        }
    }
    @RequestMapping(value = ServiceApi.Authentication.ValidToken, method = RequestMethod.POST)
    public ResponseEntity<AccessToken> validToken(@RequestParam Map<String, String> parameters) throws IllegalAccessException {
        //根据accessToken查询相应的访问授权数据行
        String clientId = parameters.get("clientId");
        String accessToken = parameters.get("accessToken");
        String api = parameters.get("api");
        if (StringUtils.isEmpty(clientId)) {
            throw new InvalidRequestException("Missing clientId");
        }
        if (StringUtils.isEmpty(accessToken)) {
            throw new InvalidRequestException("Missing accessToken");
        }
        OAuth2AccessToken auth2AccessToken = ehrTokenServices.readAccessToken(accessToken);
        if (auth2AccessToken == null) {
            throw new InvalidTokenException("Invalid accessToken");
        } else {
            if (auth2AccessToken.isExpired()) {
                throw new InvalidTokenException("Expired accessToken");
            } else {
                //判断ClientId
                OAuth2Authentication authentication = ehrTokenServices.loadAuthentication(accessToken);
                String authenticationClientId = authentication.getOAuth2Request().getClientId();
                if (authenticationClientId != null && authenticationClientId.equals(clientId)) {
                    AccessToken accessToken1 = new AccessToken();
                    if (StringUtils.isEmpty(api)) {
                        accessToken1.setAccessToken(auth2AccessToken.getValue());
                        accessToken1.setTokenType(auth2AccessToken.getTokenType());
                        accessToken1.setExpiresIn(auth2AccessToken.getExpiresIn());
                        accessToken1.setRefreshToken(auth2AccessToken.getRefreshToken().getValue());
                        accessToken1.setUser(authentication.getName());
                        accessToken1.setState(parameters.get("state"));
                        return getResponse(accessToken1);
                    } else {
                        if (ehrRedisApiAccessValidator.verificationApi(clientId, authentication.getName(), api)){
                            accessToken1.setAccessToken(auth2AccessToken.getValue());
                            accessToken1.setTokenType(auth2AccessToken.getTokenType());
                            accessToken1.setExpiresIn(auth2AccessToken.getExpiresIn());
                            accessToken1.setRefreshToken(auth2AccessToken.getRefreshToken().getValue());
                            accessToken1.setUser(authentication.getName());
                            accessToken1.setState(parameters.get("state"));
                            return getResponse(accessToken1);
                        } else {
                            throw new InvalidRequestException("Illegal api request");
                        }
                    }
                } else {
                    throw new InvalidClientException("Illegal client id");
                }
            }
        }
    }
    private void putVerificationApi(TokenRequest tokenRequest, OAuth2Authentication authentication) {
        String clientId = tokenRequest.getClientId();
        ehrRedisApiAccessValidator.putVerificationApi(clientId, authentication.getName());
    }
    /**
    protected TokenGranter getTokenGranter(String grantType) {
        if ("authorization_code".equals(grantType)) {
            return new EhrAuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, this.oAuth2RequestFactory, ehrJDBCUserSecurityService, ehrUserDetailsService);
        } else if ("password".equals(grantType)) {
            return new ResourceOwnerPasswordTokenGranter(getAuthenticationManager(), tokenServices, clientDetailsService, this.oAuth2RequestFactory);
        } else if ("refresh_token".equals(grantType)) {
            return new RefreshTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory);
        } else if ("client_credentials".equals(grantType)) {
            return new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory);
        } else if ("implicit".equals(grantType)) {
            return new ImplicitTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory);
        } else {
            throw new UnsupportedGrantTypeException("Unsupport grant_type: " + grantType);
        }
    }
    */
    @Override
    protected TokenGranter getTokenGranter() {
        return this.ehrTokenGranter;
    }
    private ResponseEntity<AccessToken> getResponse(AccessToken accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
    }
    private boolean isRefreshTokenRequest(Map<String, String> parameters) {
        return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null;
    }
    private boolean isAuthCodeRequest(Map<String, String> parameters) {
        return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.state(ehrJdbcClientDetailsService != null, "ClientDetailsService must be provided");
        Assert.state(authenticationManager != null, "AuthenticationManager must be provided");
        oAuth2RequestFactory = new DefaultOAuth2RequestFactory(ehrJdbcClientDetailsService);
    }
    private AuthenticationManager getAuthenticationManager() {
        return this.authenticationManager;
    }
    @Override
    protected WebResponseExceptionTranslator getExceptionTranslator() {
        return ehrOAuth2ExceptionTranslator;
    }
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (this.authenticationManager == null) {
            this.authenticationManager = (AuthenticationManager) applicationContext.getBean("authenticationManager");
        }
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
        LOG.warn("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
}

+ 339 - 0
src/main/java/com/yihu/ehr/oauth2/web/EhrAuthLoginEndpoint.java

@ -0,0 +1,339 @@
package com.yihu.ehr.oauth2.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yihu.ehr.constants.ErrorCode;
import com.yihu.ehr.constants.ServiceApi;
import com.yihu.ehr.model.user.EhrUserSimple;
import com.yihu.ehr.oauth2.client.FzApiClient;
import com.yihu.ehr.oauth2.model.Oauth2Failed;
import com.yihu.ehr.oauth2.model.VerifyCode;
import com.yihu.ehr.oauth2.oauth2.EhrOAuth2ExceptionTranslator;
import com.yihu.ehr.oauth2.oauth2.EhrTokenGranter;
import com.yihu.ehr.oauth2.oauth2.EhrUserDetailsService;
import com.yihu.ehr.oauth2.oauth2.jdbc.EhrJdbcClientDetailsService;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisTokenStore;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisVerifyCodeService;
import com.yihu.ehr.util.id.RandomUtil;
import com.yihu.ehr.util.rest.Envelop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by progr1mmer on 2018/4/3.
 */
@RestController
public class EhrAuthLoginEndpoint extends AbstractEndpoint {
    private static final Logger LOG = LoggerFactory.getLogger(EhrAuthLoginEndpoint.class);
    private OAuth2RequestFactory oAuth2RequestFactory;
    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private EhrJdbcClientDetailsService ehrJdbcClientDetailsService;
    @Autowired
    private EhrTokenGranter ehrTokenGranter;
    @Autowired
    private EhrOAuth2ExceptionTranslator ehrOAuth2ExceptionTranslator;
    @Autowired
    private EhrRedisTokenStore ehrRedisTokenStore;
    @Autowired
    private EhrUserDetailsService ehrUserDetailsService;
    @Autowired
    private EhrRedisVerifyCodeService ehrRedisVerifyCodeService;
    @Autowired
    private FzApiClient fzApiClient;
    @Value("${fz-gateway.url}")
    private String fzGatewayUrl;
    @Value("${fz-gateway.clientId}")
    private String fzClientId;
    @Value("${fz-gateway.clientVersion}")
    private String fzClientVersion;
    @Value("${fz-gateway.handlerId}")
    private String fzHandlerId;
    @Autowired
    private ObjectMapper objectMapper;
    @PostConstruct
    private void init() {
        super.setTokenGranter(ehrTokenGranter);
    }
    @RequestMapping(value = ServiceApi.Authentication.Login, method = RequestMethod.POST)
    public ResponseEntity<EhrUserSimple> login(@RequestParam Map<String, String> parameters, HttpServletRequest request) {
        String client_id = parameters.get("client_id");
        String scope = parameters.get("scope");
        //检查基本参数
        if (StringUtils.isEmpty(client_id)) {
            throw new InvalidRequestException("Missing clientId");
        }
        Map<String, String> param = new HashMap<>();
        if (StringUtils.isEmpty(parameters.get("verify_code"))) {
            param.put("grant_type", "password");
            param.put("password", parameters.get("password"));
        } else {
            param.put("grant_type", "verify_code");
            param.put("verify_code", parameters.get("verify_code"));
        }
        param.put("client_id", client_id);
        param.put("scope", scope);
        param.put("username", parameters.get("username"));
        EhrUserSimple ehrUserSimple = ehrUserDetailsService.loadUserSimpleByUsername(param.get("username"));
        ClientDetails authenticatedClient = ehrJdbcClientDetailsService.loadClientByClientId(client_id);
        TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(param, authenticatedClient);
        //校验 client_id 是否一致
        if (!client_id.equals(tokenRequest.getClientId())) {
            throw new InvalidClientException("Given client ID does not match authenticated client");
        }
        if (authenticatedClient != null) {
            oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
        }
        if (!StringUtils.hasText(tokenRequest.getGrantType())) {
            throw new InvalidRequestException("Missing grant type");
        }
        OAuth2AccessToken token = getTokenGranter().grant(param.get("grant_type"), tokenRequest);
        /*如果是移动端登陆则移除之前的token,
        在网关处通过HTTP状态码告知前端是过期(402)还是账号在别处登陆(403),
        实现同一账号只能在一处登陆*/
        if (request.getHeader("login-device") != null && request.getHeader("login-device").equals("mobile")) {
            ehrRedisTokenStore.removeAccessToken(token.getValue());
            ehrRedisTokenStore.removeRefreshToken(token.getRefreshToken().getValue());
            token = getTokenGranter().grant(param.get("grant_type"), tokenRequest);
        }
        if (token == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + param.get("grant_type"));
        } else {
            ehrUserSimple.setAccessToken(token.getValue());
            ehrUserSimple.setTokenType(token.getTokenType());
            ehrUserSimple.setExpiresIn(token.getExpiresIn());
            ehrUserSimple.setRefreshToken(token.getRefreshToken().getValue());
            ehrUserSimple.setUser(param.get("username"));
            ehrUserSimple.setState(parameters.get("state"));
        }
        return getResponse(ehrUserSimple);
    }
    @RequestMapping(value = ServiceApi.Authentication.Logout, method = RequestMethod.POST)
    public ResponseEntity<String> logout(@RequestParam Map<String, String> parameters, HttpServletRequest request) {
        String token = request.getHeader("token");
        if (null == token) {
            token = parameters.get("token");
        }
        OAuth2AccessToken oAuth2AccessToken = ehrRedisTokenStore.readAccessToken(token);
        if (oAuth2AccessToken != null) {
            ehrRedisTokenStore.removeAccessToken(oAuth2AccessToken.getValue());
            ehrRedisTokenStore.removeRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>("Logout", headers, HttpStatus.OK);
    }
    @RequestMapping(value = ServiceApi.Authentication.VerifyCode, method = RequestMethod.POST)
    public ResponseEntity<Envelop> verifyCode(@RequestParam Map<String, String> parameters) throws  Exception{
        Envelop envelop = new Envelop();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        if (StringUtils.isEmpty(username)){
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("手机号码【"+username+"】不能为空!");
            return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
        }
        VerifyCode verifyCode = new VerifyCode();
        //手机短信验证码
        RandomUtil randomUtil = new RandomUtil();
        String random = randomUtil.getRandomString(6);
        //发送短信
        String api = "MsgGW.Sms.send";
        String content = "尊敬的用户:欢迎使用健康上饶,您的验证码为:【" + random + "】,有效期10分钟,请尽快完成注册。若非本人操作,请忽略。";
        Map<String, String> apiParamMap = new HashMap<>();
        //手机号码
        apiParamMap.put("mobile", username);
        //业务标签
        apiParamMap.put("handlerId", fzHandlerId);
        //短信内容
        apiParamMap.put("content", content);
        //渠道号
        apiParamMap.put("clientId", fzClientId);
        String result = null;
        Envelop resultEnvelop = fzApiClient.fzInnerApi(api, objectMapper.writeValueAsString(apiParamMap), 1);
        if (resultEnvelop.isSuccessFlg()) {
            result = resultEnvelop.getObj().toString();
        }
        if (!StringUtils.isEmpty(result)) {
            Map<String, Object> resultMap = objectMapper.readValue(result, Map.class);
            Integer resultCode = 0;
            if (null != resultMap.get("Code") && !"".equals(resultMap.get("Code"))) {
                resultCode = Integer.valueOf(resultMap.get("Code").toString());
            }
            if (resultCode == 10000) {
                verifyCode.setExpiresIn(600);
                verifyCode.setNextRequestTime(60);
                //验证码有效期
                ehrRedisVerifyCodeService.store(client_id, username, random, 600000);
                envelop.setSuccessFlg(true);
                envelop.setObj(verifyCode);
            } else if(resultCode == -201){
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信已达每天限制的次数(10次)!");
            } else if(resultCode == -200){
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信发送频率太快(不能低于60s)!");
            } else {
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信验证码发送失败!");
            }
        } else {
            envelop.setSuccessFlg(false);
            envelop.setErrorCode(ErrorCode.REQUEST_NOT_COMPLETED.value());
            envelop.setErrorMsg("短信验证码发送失败!");
        }
        return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
    }
    @RequestMapping(value = ServiceApi.Authentication.VerifyCodeExpire, method = RequestMethod.POST)
    public ResponseEntity<VerifyCode> verifyCodeExpire(@RequestParam Map<String, String> parameters) {
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        VerifyCode verifyCode = new VerifyCode();
        int expiresIn = ehrRedisVerifyCodeService.getExpireTime(client_id, username);
        int nextRequestTime = 60 + (expiresIn - 600 ) > 0 ? 60 + (expiresIn - 600 ) : 0;
        verifyCode.setNextRequestTime(nextRequestTime);
        verifyCode.setExpiresIn(expiresIn);
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>(verifyCode, headers, HttpStatus.OK);
    }
    @RequestMapping(value = ServiceApi.Authentication.VerifyCodeValidate, method = RequestMethod.POST)
    public ResponseEntity<Envelop> verifyCodeValidate(@RequestParam Map<String, String> parameters) throws  Exception{
        Envelop envelop = new Envelop();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        String verifyCode = parameters.get("verify_code");
        if (StringUtils.isEmpty(verifyCode)){
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("验证码不能为空!");
            return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
        }
        boolean _verify = ehrRedisVerifyCodeService.verification(client_id, username, verifyCode);
        if (_verify){
            envelop.setSuccessFlg(true);
        } else {
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("请输入正确的验证码!");
        }
        return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
    }
    @Override
    protected TokenGranter getTokenGranter() {
        return this.ehrTokenGranter;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.state(ehrJdbcClientDetailsService != null, "ClientDetailsService must be provided");
        Assert.state(authenticationManager != null, "AuthenticationManager must be provided");
        oAuth2RequestFactory = new DefaultOAuth2RequestFactory(ehrJdbcClientDetailsService);
    }
    private ResponseEntity<EhrUserSimple> getResponse(EhrUserSimple ehrUserSimple) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>(ehrUserSimple, headers, HttpStatus.OK);
    }
    @Override
    protected WebResponseExceptionTranslator getExceptionTranslator() {
        return ehrOAuth2ExceptionTranslator;
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Oauth2Failed> handleException(Exception e) throws Exception {
        if (e instanceof UsernameNotFoundException) {
            return handleOAuth2Exception(new Oauth2Failed(HttpStatus.UNAUTHORIZED.value(), "用户未注册!"));
        } else if (e instanceof NoSuchClientException) {
            return handleOAuth2Exception(new Oauth2Failed(HttpStatus.UNAUTHORIZED.value(), "应用未注册!"));
        } else if (e instanceof InvalidGrantException) {
            if (e.getMessage().equals("Invalid verify_code")) {
                return handleOAuth2Exception(new Oauth2Failed(HttpStatus.UNAUTHORIZED.value(), "验证码有误!"));
            }
            return handleOAuth2Exception(new Oauth2Failed(HttpStatus.UNAUTHORIZED.value(), "密码有误!"));
        }
        e.printStackTrace();
        return handleSystemException(new Oauth2Failed(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统繁忙!"));
    }
    private ResponseEntity<Oauth2Failed> handleOAuth2Exception(Oauth2Failed authenticationFailed) throws IOException {
        int status = authenticationFailed.getErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        if (status == HttpStatus.UNAUTHORIZED.value()) {
            headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, authenticationFailed.getErrorMsg()));
        }
        ResponseEntity<Oauth2Failed> response = new ResponseEntity<>(authenticationFailed, headers,
                HttpStatus.valueOf(status));
        return response;
    }
    private ResponseEntity<Oauth2Failed> handleSystemException(Oauth2Failed authenticationFailed) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        ResponseEntity<Oauth2Failed> response = new ResponseEntity<>(authenticationFailed, headers,
                HttpStatus.valueOf(authenticationFailed.getErrorCode()));
        return response;
    }
}

+ 580 - 0
src/main/java/com/yihu/ehr/oauth2/web/EhrAuthorizationEndpoint.java

@ -0,0 +1,580 @@
package com.yihu.ehr.oauth2.web;
import com.yihu.ehr.oauth2.oauth2.EhrOAuth2ExceptionTranslator;
import com.yihu.ehr.oauth2.oauth2.EhrTokenGranter;
import com.yihu.ehr.oauth2.oauth2.EhrUserDetailsService;
import com.yihu.ehr.oauth2.oauth2.jdbc.*;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisApiAccessValidator;
import com.yihu.ehr.oauth2.oauth2.redis.EhrRedisTokenStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.*;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.security.Principal;
import java.util.*;
/**
 * Ehr Oauth2 Server
 * Sso & authorization_code implicit Endpoint
 *
 * 自定义授权入口。由于Spring使用了自带授权入口Bean{@link AuthorizationEndpoint},但其使用@FrameworkEndpoint标识,
 * 因此用户自定义的Controller不会与之冲突,放心地使用常规方式创建新的Controller即可。
 * <p>
 * http://stackoverflow.com/questions/29345508/spring-oauth2-custom-oauth-approval-page-at-oauth-authorize
 *
 * @author Sand
 * @version 1.0
 * @created 2016.03.04 10:56
 *
 * Modify by Progr1mmer on 2018/01/08.
 *
 */
@Controller
@SessionAttributes("authorizationRequest")
public class EhrAuthorizationEndpoint extends AbstractEndpoint {
    private static final Logger LOG = LoggerFactory.getLogger(EhrAuthorizationEndpoint.class);
    @Autowired
    private EhrOAuth2ExceptionTranslator ehrOAuth2ExceptionTranslator;
    @Autowired
    private AuthorizationCodeServices inMemoryAuthorizationCodeServices;
    /*@Autowired
    private EhrJdbcTokenStore ehrJdbcTokenStore;*/
    @Autowired
    private EhrRedisTokenStore ehrRedisTokenStore;
    @Autowired
    private EhrJdbcClientDetailsService ehrJdbcClientDetailsService;
    @Autowired
    private EhrJdbcAccessUrlService ehrJDBCAccessUrlService;
    @Autowired
    private EhrJdbcUserSecurityService ehrJDBCUserSecurityService;
    @Autowired
    private EhrUserDetailsService ehrUserDetailsService;
    @Autowired
    private EhrTokenGranter ehrTokenGranter;
    @Autowired
    private EhrRedisApiAccessValidator ehrRedisApiAccessValidator;
    private RedirectResolver redirectResolver = new DefaultRedirectResolver();
    private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
    private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
    private Object implicitLock = new Object();
    private String userApprovalPage = "forward:/oauth/confirm_access";
    private String errorPage = "forward:/oauth/error";
    @Autowired
    private AuthorizationServerEndpointsConfiguration configuration;
    @PostConstruct
    public void init() throws Exception {
        AuthorizationServerEndpointsConfigurer configurer = configuration.getEndpointsConfigurer();
        configurer.setClientDetailsService(ehrJdbcClientDetailsService);
        configurer.tokenStore(ehrRedisTokenStore);
        configurer.authorizationCodeServices(inMemoryAuthorizationCodeServices);
        FrameworkEndpointHandlerMapping mapping = configuration.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
        this.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
        this.setProviderExceptionHandler(configurer.getExceptionTranslator());
        this.setErrorPage(extractPath(mapping, "/oauth/error"));
        this.setTokenGranter(configurer.getTokenGranter());
        this.setClientDetailsService(configurer.getClientDetailsService());
        this.setAuthorizationCodeServices(inMemoryAuthorizationCodeServices);
        this.setOAuth2RequestFactory(configurer.getOAuth2RequestFactory());
        this.setOAuth2RequestValidator(configurer.getOAuth2RequestValidator());
        this.setUserApprovalHandler(configurer.getUserApprovalHandler());
    }
    /**
     * oauth2
     * @param model
     * @param sessionStatus
     * @param principal
     * @param parameters
     * @return
     */
    @RequestMapping(value = "/oauth/authorize")
    public ModelAndView authorize(Map<String, Object> model, SessionStatus sessionStatus, Principal principal, @RequestParam Map<String, String> parameters){
        // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
        // query off of the authorization request instead of referring back to the parameters map. The contents of the
        // parameters map will be stored without change in the AuthorizationRequest object once it is created.
        AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        Set<String> responseTypes = authorizationRequest.getResponseTypes();
        if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            sessionStatus.setComplete();
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        }
        if (authorizationRequest.getClientId() == null) {
            sessionStatus.setComplete();
            throw new InvalidClientException("A client id must be provided");
        }
        try {
            String userName = "unAuth";
            if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
                if (parameters.containsKey("pk")) {
                    String pk = parameters.get("pk");
                    String keyId = ehrJDBCUserSecurityService.getDefaultKeyIdSelectStatement(pk);
                    String userId = ehrJDBCUserSecurityService.getDefaultUserIdByKeyIdSelectStatement(keyId);
                    userName = ehrJDBCUserSecurityService.getDefaultUserNameByUserId(userId);
                    UserDetails userDetails = ehrUserDetailsService.loadUserByUsername(userName);
                    if (StringUtils.isEmpty(keyId) || StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
                        throw new InsufficientAuthenticationException("Illegal pk");
                    }
                    UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                    SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                    securityContext.setAuthentication(userAuth);
                    SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
                    securityContextHolderStrategy.setContext(securityContext);
                    principal = userAuth;
                } else {
                    throw new InsufficientAuthenticationException("A pk must be present.");
                }
            }
            ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
            // The resolved redirect URI is either the redirect_uri from the parameters or the one from
            // clientDetails. Either way we need to store it on the AuthorizationRequest.
            //String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
            if (!StringUtils.hasText(resolvedRedirect)) {
                throw new RedirectMismatchException("A redirectUri must be either supplied or preconfigured in the ClientDetails");
            } else {
                authorizationRequest.setRedirectUri(resolvedRedirect);
                // We intentionally only validate the parameters requested by the client (ignoring any data that may have
                // been added to the request by the manager).
                oauth2RequestValidator.validateScope(authorizationRequest, client);
                // Some systems may allow for approval decisions to be remembered or approved by default. Check for
                // such logic here, and set the approved flag on the authorization request accordingly.
                authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
                        (Authentication) principal);
                // TODO: is this call necessary?
                boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
                authorizationRequest.setApproved(approved);
                // Validation is all done, so we can check for auto approval...
                if (authorizationRequest.isApproved()) {
                    if (responseTypes.contains("token")) {
                        ModelAndView implicitModelAndView = getImplicitGrantResponse(authorizationRequest);
                        ehrRedisApiAccessValidator.putVerificationApi(authorizationRequest.getClientId(), userName);
                        return implicitModelAndView;
                    }
                    if (responseTypes.contains("code")) {
                        return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
                                (Authentication) principal));
                    }
                }
                // Place auth request into the model so that it is stored in the session
                // for approveOrDeny to use. That way we make sure that auth request comes from the session,
                // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
                model.put("authorizationRequest", authorizationRequest);
                return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
            }
        } catch (RuntimeException e) {
            sessionStatus.setComplete();
            throw e;
        }
    }
    /**
     * 单点登陆 - 简化模式
     * @param model
     * @param sessionStatus
     * @param principal
     * @param parameters
     * @return
     */
    @RequestMapping(value = "/oauth/sso")
    public ModelAndView sso (Map<String, Object> model, SessionStatus sessionStatus, Principal principal, @RequestParam Map<String, String> parameters){
        // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
        // query off of the authorization request instead of referring back to the parameters map. The contents of the
        // parameters map will be stored without change in the AuthorizationRequest object once it is created.
        AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        Set<String> responseTypes = authorizationRequest.getResponseTypes();
        if (!responseTypes.contains("token")) {
            sessionStatus.setComplete();
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        }
        if (authorizationRequest.getClientId() == null) {
            sessionStatus.setComplete();
            throw new InvalidClientException("A client id must be provided");
        }
        try {
            if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
                if (parameters.containsKey("token")) {
                    String token = parameters.get("token");
                    OAuth2Authentication authentication = ehrRedisTokenStore.readAuthentication(token);
                    if (authentication != null) {
                        String userName = authentication.getName();
                        UserDetails userDetails = ehrUserDetailsService.loadUserByUsername(userName);
                        UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                        SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                        securityContext.setAuthentication(userAuth);
                        SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
                        securityContextHolderStrategy.setContext(securityContext);
                        principal = userAuth;
                    } else {
                        throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorization can be completed.");
                    }
                } else {
                    throw new InsufficientAuthenticationException("Token must be provided.");
                }
            }
            ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
            // The resolved redirect URI is either the redirect_uri from the parameters or the one from
            // clientDetails. Either way we need to store it on the AuthorizationRequest.
            //String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String redirectUriParameter = ehrJDBCAccessUrlService.getValidUrl(authorizationRequest.getClientId(), authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI));
            String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
            if (!StringUtils.hasText(resolvedRedirect)) {
                throw new RedirectMismatchException(
                        "A redirectUri must be either supplied or preconfigured in the ClientDetails");
            }
            authorizationRequest.setRedirectUri(ehrJDBCAccessUrlService.getRealUrl(authorizationRequest.getClientId(), authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI)));
            // We intentionally only validate the parameters requested by the client (ignoring any data that may have
            // been added to the request by the manager).
            oauth2RequestValidator.validateScope(authorizationRequest, client);
            // Some systems may allow for approval decisions to be remembered or approved by default. Check for
            // such logic here, and set the approved flag on the authorization request accordingly.
            authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
                    (Authentication) principal);
            // TODO: is this call necessary?
            boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
            authorizationRequest.setApproved(approved);
            // Validation is all done, so we can check for auto approval...
            if (authorizationRequest.isApproved()) {
                if (responseTypes.contains("token")) {
                    return getImplicitGrantResponse(authorizationRequest);
                }
            }
            // Place auth request into the model so that it is stored in the session
            // for approveOrDeny to use. That way we make sure that auth request comes from the session,
            // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
            model.put("authorizationRequest", authorizationRequest);
            return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
        } catch (RuntimeException e) {
            sessionStatus.setComplete();
            throw e;
        }
    }
    @RequestMapping("/oauth/confirm_access")
    public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
        if (request.getAttribute("_csrf") != null) {
            model.put("_csrf", request.getAttribute("_csrf"));
        } else {
            model.put("_csrf", "");
        }
        String scopes[] = request.getParameter("scope") == null ? new String[0] : request.getParameter("scope").split("\\+");
        model.put("scopes", scopes);
        return new ModelAndView("/oauth/confirm_access", model);
    }
    @RequestMapping("/oauth/error")
    public ModelAndView handleError(HttpServletRequest request) {
        Map<String, Object> model = new HashMap<String, Object>();
        Object error = request.getAttribute("error");
        if (error == null) {
            error = Collections.singletonMap("summary", "Unknown error");
        }
        model.put("error", error);
        return new ModelAndView("/oauth/error", model);
    }
    public void setUserApprovalPage(String userApprovalPage) {
        this.userApprovalPage = userApprovalPage;
    }
    public void setAuthorizationCodeServices(AuthorizationCodeServices authorizationCodeServices) {
        this.inMemoryAuthorizationCodeServices = authorizationCodeServices;
    }
    public void setRedirectResolver(RedirectResolver redirectResolver) {
        this.redirectResolver = redirectResolver;
    }
    public void setOAuth2RequestValidator(OAuth2RequestValidator oauth2RequestValidator) {
        this.oauth2RequestValidator = oauth2RequestValidator;
    }
    public void setUserApprovalHandler(UserApprovalHandler userApprovalHandler) {
        this.userApprovalHandler = userApprovalHandler;
    }
    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }
    private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
        String path = mapping.getPath(page);
        if (path.contains(":")) {
            return path;
        }
        return "forward:" + path;
    }
    // We need explicit approval from the user.
    private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
                                                     AuthorizationRequest authorizationRequest, Authentication principal) {
        logger.debug("Loading user approval page: " + userApprovalPage);
        model.putAll(userApprovalHandler.getUserApprovalRequest(authorizationRequest, principal));
        return new ModelAndView(userApprovalPage, model);
    }
    private View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) {
        try {
            //url+code
            String url=getSuccessfulRedirect(authorizationRequest,
                    generateCode(authorizationRequest, authUser));
            //重定向到目标网站
            return new RedirectView(url,false, true, false);
        } catch (OAuth2Exception e) {
            return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, false), false, true, false);
        }
    }
    private String appendAccessToken(AuthorizationRequest authorizationRequest, OAuth2AccessToken accessToken) {
        Map<String, Object> vars = new LinkedHashMap<String, Object>();
        Map<String, String> keys = new HashMap<String, String>();
        if (accessToken == null) {
            throw new InvalidRequestException("An implicit grant could not be made");
        }
        vars.put("access_token", accessToken.getValue());
        vars.put("token_type", accessToken.getTokenType());
        String state = authorizationRequest.getState();
        if (state != null) {
            vars.put("state", state);
        }
        Date expiration = accessToken.getExpiration();
        if (expiration != null) {
            long expires_in = (expiration.getTime() - System.currentTimeMillis()) / 1000;
            vars.put("expires_in", expires_in);
        }
        Map<String,String> map = authorizationRequest.getRequestParameters();
        vars.put("redirect_uri", map.get("redirect_uri"));
        String originalScope = authorizationRequest.getRequestParameters().get(OAuth2Utils.SCOPE);
        if (originalScope == null || !OAuth2Utils.parseParameterList(originalScope).equals(accessToken.getScope())) {
            vars.put("scope", OAuth2Utils.formatParameterList(accessToken.getScope()));
        }
        Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
        for (String key : additionalInformation.keySet()) {
            Object value = additionalInformation.get(key);
            if (value != null) {
                keys.put("extra_" + key, key);
                vars.put("extra_" + key, value);
            }
        }
        // Do not include the refresh token (even if there is one)
        return append(authorizationRequest.getRedirectUri(), vars, keys, true);
    }
    private String generateCode(AuthorizationRequest authorizationRequest, Authentication authentication)
            throws AuthenticationException {
        try {
            OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
            OAuth2Authentication combinedAuth = new OAuth2Authentication(storedOAuth2Request, authentication);
            String code = inMemoryAuthorizationCodeServices.createAuthorizationCode(combinedAuth);
            return code;
        } catch (OAuth2Exception e) {
            if (authorizationRequest.getState() != null) {
                e.addAdditionalInformation("state", authorizationRequest.getState());
            }
            throw e;
        }
    }
    private String getSuccessfulRedirect(AuthorizationRequest authorizationRequest, String authorizationCode) {
        if (authorizationCode == null) {
            throw new IllegalStateException("No authorization code found in the current request scope.");
        }
        Map<String, String> query = new LinkedHashMap<String, String>();
        query.put("code", authorizationCode);
        String state = authorizationRequest.getState();
        if (state != null) {
            query.put("state", state);
        }
        return append(authorizationRequest.getRedirectUri(), query, false);
    }
    private String getUnsuccessfulRedirect(AuthorizationRequest authorizationRequest, OAuth2Exception failure,
                                           boolean fragment) {
        if (authorizationRequest == null || authorizationRequest.getRedirectUri() == null) {
            // we have no redirect for the user. very sad.
            throw new UnapprovedClientAuthenticationException("Authorization failure, and no redirect URI.", failure);
        }
        Map<String, String> query = new LinkedHashMap<String, String>();
        query.put("error", failure.getOAuth2ErrorCode());
        query.put("error_description", failure.getMessage());
        if (authorizationRequest.getState() != null) {
            query.put("state", authorizationRequest.getState());
        }
        if (failure.getAdditionalInformation() != null) {
            for (Map.Entry<String, String> additionalInfo : failure.getAdditionalInformation().entrySet()) {
                query.put(additionalInfo.getKey(), additionalInfo.getValue());
            }
        }
        return append(authorizationRequest.getRedirectUri(), query, fragment);
    }
    private String append(String base, Map<String, ?> query, boolean fragment) {
        return append(base, query, null, fragment);
    }
    private String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
        UriComponentsBuilder template = UriComponentsBuilder.newInstance();
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
        URI redirectUri;
        try {
            // assume it's encoded to start with (if it came in over the wire)
            redirectUri = builder.build(true).toUri();
        } catch (Exception e) {
            // ... but allow client registrations to contain hard-coded non-encoded values
            redirectUri = builder.build().toUri();
            builder = UriComponentsBuilder.fromUri(redirectUri);
        }
        template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
                .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
        if (fragment) {
            StringBuilder values = new StringBuilder();
            if (redirectUri.getFragment() != null) {
                String append = redirectUri.getFragment();
                values.append(append);
            }
            for (String key : query.keySet()) {
                if (values.length() > 0) {
                    values.append("&");
                }
                String name = key;
                if (keys != null && keys.containsKey(key)) {
                    name = keys.get(key);
                }
                values.append(name + "={" + key + "}");
            }
            if (values.length() > 0) {
                template.fragment(values.toString());
            }
            UriComponents encoded = template.build().expand(query).encode();
            builder.fragment(encoded.getFragment());
        } else {
            for (String key : query.keySet()) {
                String name = key;
                if (keys != null && keys.containsKey(key)) {
                    name = keys.get(key);
                }
                template.queryParam(name, "{" + key + "}");
            }
            template.fragment(redirectUri.getFragment());
            UriComponents encoded = template.build().expand(query).encode();
            builder.query(encoded.getQuery());
        }
        return builder.build().toUriString();
    }
    // We can grant a token and return it with implicit approval.
    private ModelAndView getImplicitGrantResponse(AuthorizationRequest authorizationRequest) {
        try {
            TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(authorizationRequest, "implicit");
            OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
            OAuth2AccessToken accessToken = getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request);
            if (accessToken == null) {
                throw new UnsupportedResponseTypeException("Unsupported response type: token");
            }
            return new ModelAndView(new RedirectView(appendAccessToken(authorizationRequest, accessToken), false, true,
                    false));
        } catch (OAuth2Exception e) {
            return new ModelAndView(new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, true), false,
                    true, false));
        }
    }
    private OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest, OAuth2Request storedOAuth2Request) {
        OAuth2AccessToken accessToken = null;
        // These 1 method calls have to be atomic, otherwise the ImplicitGrantService can have a race condition where
        // one thread removes the token request before another has a chance to redeem it.
        synchronized (this.implicitLock) {
            accessToken = getTokenGranter().grant("implicit", new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
        }
        return accessToken;
    }
    @Override
    protected TokenGranter getTokenGranter() {
        return this.ehrTokenGranter;
    }
    @Override
    protected WebResponseExceptionTranslator getExceptionTranslator() {
        return ehrOAuth2ExceptionTranslator;
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
        LOG.warn("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
}

+ 37 - 0
src/main/resources/application.yml

@ -0,0 +1,37 @@
server:
  port: ${svr-authentication.server.port}
spring:
  redis:
    database: 0 # Database index used by the connection factory.
    timeout: 0 # Connection timeout in milliseconds.
    pool:
      max-active: 8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
      max-idle: 8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
      max-wait: -1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
      min-idle: 1 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    max-active: 20
    max-idle: 8
    min-idle: 8
    validation-query: SELECT 1
    test-on-borrow: true
---
spring:
  profiles: dev
  datasource:
    url: jdbc:mysql://172.19.103.50:3306/healtharchive?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: chenweishan
    password: chenweishan
  redis:
    host: 172.19.103.47
    port: 6379
    password: redis!@456
fz-gateway:
  url: http://172.19.103.73:10001/fzGateway/WSGW/rest
  clientId: 9000401
  clientVersion: 1.0
  handlerId: 50871

+ 29 - 0
src/main/resources/banner.txt

@ -0,0 +1,29 @@
                                   _oo8oo_
                                  o8888888o
                                  88" . "88
                                  (| -_- |)
                                  0\  =  /0
                                ___/'==='\___
                              .' \\|     |// '.
                             / \\|||  :  |||// \
                            / _||||| -:- |||||_ \
                           |   | \\\  -  /// |   |
                           | \_|  ''\---/''  |_/ |
                           \  .-\__  '-'  __/-.  /
                         ___'. .'  /--.--\  '. .'___
                      ."" '<  '.___\_<|>_/___.'  >' "".
                     | | :  `- \`.:`\ _ /`:.`/ -`  : | |
                     \  \ `-.   \_ __\ /__ _/   .-` /  /
                 =====`-.____`.___ \_____/ ___.`____.-`=====
                                   `=---=`
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                      佛祖保佑     永不宕机     永无Bug
 __   _      ___        __    _    _____  _     ____  _     _____  _   __     __   _____  _   ___   _
( (` \ \  / | |_)      / /\  | | |  | |  | |_| | |_  | |\ |  | |  | | / /`   / /\   | |  | | / / \ | |\ |
_)_)  \_\/  |_| \     /_/--\ \_\_/  |_|  |_| | |_|__ |_| \|  |_|  |_| \_\_, /_/--\  |_|  |_| \_\_/ |_| \|

+ 23 - 0
src/main/resources/bootstrap.yml

@ -0,0 +1,23 @@
spring:
  application:
    name: svr-authentication
  cloud:
    config:
      username: user
      password: configuration
---
spring:
  profiles: dev
  cloud:
    config:
      uri: ${spring.config.uri:http://172.19.103.73:1221}
      label: ${spring.config.label:dev}
---
spring:
  profiles: prod
  cloud:
    config:
      uri: ${spring.config.uri}
      label: ${spring.config.label}