新增应用授权

1.x
tanghc 6 years ago
parent 6e36165c20
commit 467ec052eb
  1. 4
      changelog.md
  2. 16
      sop-1.6.0.sql
  3. 2
      sop-admin/sop-admin-server/pom.xml
  4. 2
      sop-common/pom.xml
  5. 4
      sop-common/sop-gateway-common/pom.xml
  6. 4
      sop-common/sop-service-common/pom.xml
  7. 1
      sop-example/pom.xml
  8. 29
      sop-example/sop-auth/.gitignore
  9. 127
      sop-example/sop-auth/pom.xml
  10. 9
      sop-example/sop-auth/readme.md
  11. 15
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/SopAuthApplication.java
  12. 44
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AccessToken.java
  13. 14
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/AppIdManager.java
  14. 30
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenParam.java
  15. 39
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/FetchTokenResult.java
  16. 46
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Config.java
  17. 93
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Manager.java
  18. 44
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OAuth2Service.java
  19. 22
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/OpenUser.java
  20. 40
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/RefreshToken.java
  21. 29
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/TokenPair.java
  22. 25
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/exception/LoginErrorException.java
  23. 33
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/AppIdManagerImpl.java
  24. 135
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerCache.java
  25. 171
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ManagerRedis.java
  26. 284
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/auth/impl/OAuth2ServiceImpl.java
  27. 20
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/config/OpenServiceConfig.java
  28. 54
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/CallbackController.java
  29. 85
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/controller/OAuth2Controller.java
  30. 32
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/IsvInfo.java
  31. 48
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/entity/UserInfo.java
  32. 11
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/IsvInfoMapper.java
  33. 11
      sop-example/sop-auth/src/main/java/com/gitee/sop/sopauth/mapper/UserInfoMapper.java
  34. 35
      sop-example/sop-auth/src/main/resources/application-dev.yml
  35. 3
      sop-example/sop-auth/src/main/resources/application.yml
  36. 19
      sop-example/sop-auth/src/main/resources/templates/oauth2login.html
  37. 40
      sop-example/sop-auth/src/test/java/com/gitee/sop/sopauth/RefreshTokenTest.java
  38. 16
      sop-example/sop-auth/src/test/java/com/gitee/sop/sopauth/SopAuthApplicationTests.java
  39. 2
      sop-example/sop-book/sop-book-web/pom.xml
  40. 2
      sop-example/sop-easyopen/pom.xml
  41. 2
      sop-example/sop-springmvc/pom.xml
  42. 2
      sop-example/sop-story/sop-story-web/pom.xml
  43. 2
      sop-gateway/pom.xml
  44. 28
      sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/model/OpenAuthTokenAppModel.java
  45. 13
      sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/request/OpenAuthTokenAppRequest.java
  46. 2
      sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/response/GetStoryResponse.java
  47. 30
      sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/response/OpenAuthTokenAppResponse.java
  48. BIN
      sop-website/website-front/assets/image/auth.png
  49. 288
      sop-website/website-front/pages/doc/auth.html
  50. 2
      sop-website/website-front/pages/doc/doc.html
  51. 19
      sop.sql

@ -1,5 +1,9 @@
# changelog # changelog
## 1.6.0
- 新增应用授权
## 1.5.0 ## 1.5.0
- admin新增signType字段 - admin新增signType字段

@ -0,0 +1,16 @@
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
`nickname` varchar(64) NOT NULL DEFAULT '' COMMENT '昵称',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_unamepwd` (`username`,`password`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';
INSERT INTO `user_info` ( `username`, `password`, `nickname`, `gmt_create`, `gmt_modified`) VALUES
('zhangsan','123456','张三','2019-04-27 08:32:57','2019-04-27 08:32:57');

@ -43,7 +43,7 @@
<dependency> <dependency>
<groupId>net.oschina.durcframework</groupId> <groupId>net.oschina.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId> <artifactId>fastmybatis-spring-boot-starter</artifactId>
<version>1.6.0</version> <version>1.7.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId> <artifactId>sop-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>

@ -5,11 +5,11 @@
<parent> <parent>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId> <artifactId>sop-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>sop-gateway-common</artifactId> <artifactId>sop-gateway-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>sop-gateway-common</name> <name>sop-gateway-common</name>

@ -6,11 +6,11 @@
<parent> <parent>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId> <artifactId>sop-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>sop-service-common</artifactId> <artifactId>sop-service-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>sop-service-common</name> <name>sop-service-common</name>

@ -9,6 +9,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>sop-auth</module>
<module>sop-story</module> <module>sop-story</module>
<module>sop-book</module> <module>sop-book</module>
<module>sop-easyopen</module> <module>sop-easyopen</module>

@ -0,0 +1,29 @@
HELP.md
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/
### VS Code ###
.vscode/

@ -0,0 +1,127 @@
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-auth</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>sop-auth</name>
<description>授权程序</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<oltu.version>0.31</oltu.version>
</properties>
<dependencies>
<!-- sop相关配置 -->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.6.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- sop相关配置 end-->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sdk-java</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- oauth2服务端 -->
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
<version>${oltu.version}</version>
</dependency>
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
<version>${oltu.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,9 @@
# 应用授权
- 启动注册中心、网关、本服务(sop-auth)
- 浏览器访问:http://localhost:8087/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback
- 输入用户名密码登录,这里是`zhangsan/123456`
授权接口在`OAuth2Controller`中,查看回调在`CallbackController`中
token的维护,重点关注`OAuth2ManagerRedis.java`

@ -0,0 +1,15 @@
package com.gitee.sop.sopauth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class SopAuthApplication {
public static void main(String[] args) {
SpringApplication.run(SopAuthApplication.class, args);
}
}

@ -0,0 +1,44 @@
package com.gitee.sop.sopauth.auth;
/**
* @author tanghc
*/
public class AccessToken {
/** 过期时间,毫秒 */
private long expireTimeMillis;
private OpenUser openUser;
/**
* @param expireIn 有效时长
* @param openUser
*/
public AccessToken(long expireIn, OpenUser openUser) {
super();
if(expireIn <= 0) {
throw new IllegalArgumentException("expireIn必须大于0");
}
this.expireTimeMillis = System.currentTimeMillis() + (expireIn * 1000);
this.openUser = openUser;
}
public boolean isExpired() {
return expireTimeMillis < System.currentTimeMillis();
}
public long getExpireTimeMillis() {
return expireTimeMillis;
}
public void setExpireTimeMillis(long expireTimeMillis) {
this.expireTimeMillis = expireTimeMillis;
}
public OpenUser getOpenUser() {
return openUser;
}
public void setOpenUser(OpenUser openUser) {
this.openUser = openUser;
}
}

@ -0,0 +1,14 @@
package com.gitee.sop.sopauth.auth;
/**
* @author tanghc
*/
public interface AppIdManager {
/**
* 是否是合法的appId
*
* @param appId
* @return true:合法
*/
boolean isValidAppId(String appId);
}

@ -0,0 +1,30 @@
package com.gitee.sop.sopauth.auth;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 获取accessToken参数对象
* @author tanghc
*/
@Data
public class FetchTokenParam {
/**
* 授权类型<br>
* 如果使用app_auth_code换取token则为authorization_code
* 如果使用refresh_token换取新的token则为refresh_token
*/
@NotBlank(message = "授权类型不能为空")
private String grant_type;
/**
* 授权码.与refresh_token二选一用户对应用授权后得到即第一步中开发者获取到的app_auth_code值
*/
private String code;
/**
* 刷新令牌.与code二选一可为空刷新令牌时使用
*/
private String refresh_token;
}

@ -0,0 +1,39 @@
package com.gitee.sop.sopauth.auth;
import lombok.Data;
/**
* 使用app_auth_code换取app_auth_token返回结果
* @author tanghc
*/
@Data
public class FetchTokenResult {
/**
* 授权令牌
*/
private String app_auth_token;
/**
* 用户id
*/
private String user_id;
/**
* 令牌有效期. -1:永久有效
*/
private long expires_in = -1;
/**
* 刷新令牌有效期. -1:永久有效
*/
private long re_expires_in = -1;
/**
* 刷新令牌时使用
* 刷新令牌后我们会保证老的app_auth_token从刷新开始10分钟内可继续使用请及时替换为最新token
*/
private String app_refresh_token;
private String error;
private String error_description;
}

@ -0,0 +1,46 @@
package com.gitee.sop.sopauth.auth;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class OAuth2Config {
private static OAuth2Config instance = new OAuth2Config();
public static OAuth2Config getInstance() {
return instance;
}
public static void setInstance(OAuth2Config instance) {
OAuth2Config.instance = instance;
}
/**
* 应用授权的app_auth_code唯一的app_auth_code使用一次后失效一天从生成app_auth_code开始的24小时未被使用自动过期
* app_auth_token永久有效
*/
private int codeTimeoutSeconds = 86400;
/**
* accessToken有效时间单位秒
* -1 永久有效
*/
private int accessTokenExpiresIn = -1;
/**
* app_refresh_token有效时间单位秒accessToken有效时间的3倍当小于0永久有效
*/
private int refreshTokenExpiresIn = accessTokenExpiresIn * 3;
/**
* 刷新令牌后我们会保证老的app_auth_token从刷新开始10分钟内可继续使用请及时替换为最新token
*/
private int afterRefreshExpiresIn = 60 * 10;
/**
* 登录视图页面用于mvc视图loginView
*/
private String oauth2LoginUri = "/oauth2/login";
}

@ -0,0 +1,93 @@
package com.gitee.sop.sopauth.auth;
import com.gitee.sop.sopauth.auth.exception.LoginErrorException;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 认证服务需要自己实现
* @author tanghc
*
*/
public interface OAuth2Manager {
/**
* 添加 auth code
*
* @param authCode
* code值
* @param authUser
* 用户
*/
void addAuthCode(String authCode, OpenUser authUser);
/**
* 添加accessToken
* @param accessToken token值
* @param refreshToken refreshToken
* @param authUser 用户
*/
void addAccessToken(String accessToken, String refreshToken, OpenUser authUser);
/**
* 删除这个accessToken
* @param accessToken
*/
void removeAccessToken(String accessToken);
/**
* 删除这个refreshToken
* @param refreshToken
*/
void removeRefreshToken(String refreshToken);
/**
* 获取RefreshToken
* @param refreshToken
* @return 返回Token信息
*/
RefreshToken getRefreshToken(String refreshToken);
/**
* 验证auth code是否有效
*
* @param authCode
* @return 无效返回false
*/
boolean checkAuthCode(String authCode);
/**
* 根据auth code获取用户
*
* @param authCode
* @return 返回用户
*/
OpenUser getUserByAuthCode(String authCode);
/**
* 根据access token获取用户名
*
* @param accessToken
* token值
* @return 返回用户
*/
OpenUser getUserByAccessToken(String accessToken);
/**
* 返回accessToken中追加的参数
* @param user
* @return 返回追加的参数
*/
Map<String, String> getParam(OpenUser user);
/**
* 用户登录需判断是否已经登录
* @param request
* @return 返回用户对象
* @throws LoginErrorException 登录失败异常
*/
OpenUser login(HttpServletRequest request) throws LoginErrorException;
}

@ -0,0 +1,44 @@
package com.gitee.sop.sopauth.auth;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URISyntaxException;
/**
* @author tanghc
*/
public interface OAuth2Service {
/**
* oauth2授权,获取code.
* <pre>
* 1首先通过如http://localhost:8080/api/authorize?client_id=test&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2callback访问授权页面;
2该控制器首先检查clientId是否正确如果错误将返回相应的错误信息
3然后判断用户是否登录了如果没有登录首先到登录页面登录
4登录成功后生成相应的code即授权码然后重定向到客户端地址如http://localhost:8080/oauth2callback?code=6d250650831fea227749f49a5b49ccad;在重定向到的地址中会带上code参数(授权码),接着客户端可以根据授权码去换取accessToken。
* </pre>
*
* @param request req
* @param response resp
* @param authConfig 配置
* @return 返回响应内容
* @throws URISyntaxException
* @throws OAuthSystemException
*/
OAuthResponse authorize(HttpServletRequest request, HttpServletResponse response, OAuth2Config authConfig)
throws URISyntaxException, OAuthSystemException;
/**
* 通过code获取accessToken.
* @param fetchTokenParam
* @param authConfig 配置项
* @return 返回响应内容
* @throws URISyntaxException
* @throws OAuthSystemException
*/
FetchTokenResult accessToken(FetchTokenParam fetchTokenParam, OAuth2Config authConfig);
}

@ -0,0 +1,22 @@
package com.gitee.sop.sopauth.auth;
import java.io.Serializable;
/**
* 如果要使用oauth2功能自己的用户类需要实现这个对象
*
* @author tanghc
*/
public interface OpenUser extends Serializable {
/**
* 返回用户id
* @return
*/
String getUserId();
/**
* 返回用户名
*
* @return 返回用户名
*/
String getUsername();
}

@ -0,0 +1,40 @@
package com.gitee.sop.sopauth.auth;
import java.io.Serializable;
/**
* @author tanghc
*/
public class RefreshToken implements Serializable {
private static final long serialVersionUID = 1L;
private String accessToken;
private OpenUser openUser;
public RefreshToken(String accessToken, OpenUser openUser) {
super();
this.accessToken = accessToken;
this.openUser = openUser;
}
public RefreshToken() {
super();
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public OpenUser getOpenUser() {
return openUser;
}
public void setOpenUser(OpenUser openUser) {
this.openUser = openUser;
}
}

@ -0,0 +1,29 @@
package com.gitee.sop.sopauth.auth;
/**
* 存放accessToken和refreshToken
*
* @author tanghc
*
*/
public class TokenPair {
private String accessToken;
private String refreshToken;
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;
}
}

@ -0,0 +1,25 @@
package com.gitee.sop.sopauth.auth.exception;
/**
* @author tanghc
*/
public class LoginErrorException extends Exception {
private static final long serialVersionUID = -6721499454527023339L;
public LoginErrorException() {
super();
}
public LoginErrorException(String message, Throwable cause) {
super(message, cause);
}
public LoginErrorException(String message) {
super(message);
}
public LoginErrorException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,33 @@
package com.gitee.sop.sopauth.auth.impl;
import com.gitee.sop.sopauth.auth.AppIdManager;
import com.gitee.sop.sopauth.entity.IsvInfo;
import com.gitee.sop.sopauth.mapper.IsvInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author tanghc
*/
@Service
@Slf4j
public class AppIdManagerImpl implements AppIdManager {
public static final int FORBIDDEN = 2;
@Autowired
private IsvInfoMapper isvInfoMapper;
@Override
public boolean isValidAppId(String appId) {
IsvInfo isvInfo = isvInfoMapper.getByColumn("app_key", appId);
if (isvInfo == null) {
return false;
}
if (isvInfo.getStatus().intValue() == FORBIDDEN) {
log.error("appId已禁用:{}", appId);
return false;
}
return true;
}
}

@ -0,0 +1,135 @@
package com.gitee.sop.sopauth.auth.impl;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.sopauth.auth.OAuth2Config;
import com.gitee.sop.sopauth.auth.OAuth2Manager;
import com.gitee.sop.sopauth.auth.OpenUser;
import com.gitee.sop.sopauth.auth.RefreshToken;
import com.gitee.sop.sopauth.auth.exception.LoginErrorException;
import com.gitee.sop.sopauth.entity.UserInfo;
import com.gitee.sop.sopauth.mapper.UserInfoMapper;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* oauth2管理默认谷歌缓存实现跟redis实现只能用一个正式环境推荐使用redis保存
* @author tanghc
*
*/
@Service
public class OAuth2ManagerCache implements OAuth2Manager {
/**
* 应用授权的app_auth_code唯一的app_auth_code使用一次后失效一天从生成app_auth_code开始的24小时未被使用自动过期
* app_auth_token永久有效
*/
private int codeTimeoutSeconds = OAuth2Config.getInstance().getCodeTimeoutSeconds();
/**
* accessToken过期时间
* -1 永久有效
*/
private int accessTokenTimeoutSeconds = OAuth2Config.getInstance().getAccessTokenExpiresIn();
private int refreshTokenTimeoutSeconds = OAuth2Config.getInstance().getRefreshTokenExpiresIn();
private LoadingCache<String, OpenUser> codeCache = buildCache(codeTimeoutSeconds);
private LoadingCache<String, OpenUser> accessTokenCache = buildCache(accessTokenTimeoutSeconds);
private LoadingCache<String, RefreshToken> refreshTokenCache = buildCache(refreshTokenTimeoutSeconds);
@Autowired
private UserInfoMapper userInfoMapper;
private static <T> LoadingCache<String, T> buildCache(int timeout) {
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
if (timeout > 0) {
cacheBuilder.expireAfterAccess(timeout, TimeUnit.SECONDS);
}
return cacheBuilder
.build(new CacheLoader<String, T>() {
@Override
public T load(String key) throws Exception {
return null;
}
});
}
@Override
public void addAuthCode(String authCode, OpenUser authUser) {
codeCache.put(authCode, authUser);
}
@Override
public void addAccessToken(String accessToken, String refreshToken, OpenUser authUser) {
accessTokenCache.put(accessToken, authUser);
refreshTokenCache.put(refreshToken, new RefreshToken(accessToken, authUser));
}
@Override
public void removeAccessToken(String accessToken) {
accessTokenCache.asMap().remove(accessToken);
}
@Override
public void removeRefreshToken(String refreshToken) {
refreshTokenCache.asMap().remove(refreshToken);
}
@Override
public RefreshToken getRefreshToken(String refreshToken) {
return refreshTokenCache.getIfPresent(refreshToken);
}
@Override
public boolean checkAuthCode(String authCode) {
return codeCache.asMap().containsKey(authCode);
}
@Override
public OpenUser getUserByAuthCode(String authCode) {
return codeCache.getIfPresent(authCode);
}
@Override
public OpenUser getUserByAccessToken(String accessToken) {
return accessTokenCache.getIfPresent(accessToken);
}
@Override
public Map<String, String> getParam(OpenUser user) {
Map<String, String> map = new HashMap<>();
map.put("username", user.getUsername());
return map;
}
@Override
public OpenUser login(HttpServletRequest request) throws LoginErrorException {
// 这里应该先检查用户有没有登录,如果登录直接返回openUser
String username = request.getParameter("username");
String password = request.getParameter("password");
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
throw new LoginErrorException("用户名密码不能为空");
}
Query query = new Query();
query.eq("username", username)
.eq("password", password);
UserInfo userInfo = userInfoMapper.getByQuery(query);
if(userInfo == null) {
throw new LoginErrorException("用户名密码不正确");
}
return userInfo;
}
}

@ -0,0 +1,171 @@
package com.gitee.sop.sopauth.auth.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.sopauth.auth.OAuth2Config;
import com.gitee.sop.sopauth.auth.OAuth2Manager;
import com.gitee.sop.sopauth.auth.OpenUser;
import com.gitee.sop.sopauth.auth.RefreshToken;
import com.gitee.sop.sopauth.auth.exception.LoginErrorException;
import com.gitee.sop.sopauth.entity.UserInfo;
import com.gitee.sop.sopauth.mapper.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
*
* oauth2管理redis实现这个类跟OAuth2ManagerCache类只能用一个
* 如果要用这个类注释掉OAuth2ManagerCache的@Service
* 启用这个类的@Service
*/
//@Service
public class OAuth2ManagerRedis implements OAuth2Manager {
private static String CODE_PREFIX = "com.gitee.sop.oauth2_code:";
private static String ACCESS_TOKEN_PREFIX = "com.gitee.sop.oauth2_access_token:";
private static String REFRESH_TOKEN_PREFIX = "com.gitee.sop.oauth2_refresh_token:";
private int codeTimeoutSeconds = OAuth2Config.getInstance().getCodeTimeoutSeconds();
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private UserInfoMapper userInfoMapper;
public static String getCodeKey(String code) {
return CODE_PREFIX + code;
}
public static String getAccessTokenKey(String accessToken) {
return ACCESS_TOKEN_PREFIX + accessToken;
}
public static String getRefreshTokenKey(String refreshToken) {
return REFRESH_TOKEN_PREFIX + refreshToken;
}
@Override
public void addAuthCode(String authCode, OpenUser authUser) {
redisTemplate.opsForValue().set(getCodeKey(authCode),
JSON.toJSONString(authUser),
codeTimeoutSeconds,
TimeUnit.SECONDS);
}
@Override
public void addAccessToken(String accessToken, String refreshToken, OpenUser authUser) {
// 存accessToken
long expiresIn = OAuth2Config.getInstance().getAccessTokenExpiresIn();
long reExpiresIn = OAuth2Config.getInstance().getRefreshTokenExpiresIn();
if (expiresIn > 0) {
redisTemplate.opsForValue().set(getAccessTokenKey(accessToken), JSON.toJSONString(authUser), expiresIn, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(getAccessTokenKey(accessToken), JSON.toJSONString(authUser));
}
// 存refreshToken
if (reExpiresIn > 0) {
redisTemplate.opsForValue().set(
getRefreshTokenKey(refreshToken),
JSON.toJSONString(new RefreshToken(accessToken, authUser)),
reExpiresIn, // refreshToken过期时间
TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(
getRefreshTokenKey(refreshToken),
JSON.toJSONString(new RefreshToken(accessToken, authUser)));
}
}
@Override
public void removeAccessToken(String accessToken) {
String accessTokenKey = getAccessTokenKey(accessToken);
int afterRefreshExpiresIn = OAuth2Config.getInstance().getAfterRefreshExpiresIn();
redisTemplate.expire(accessTokenKey, afterRefreshExpiresIn, TimeUnit.SECONDS);
}
@Override
public void removeRefreshToken(String refreshToken) {
redisTemplate.delete(getRefreshTokenKey(refreshToken));
}
@Override
public RefreshToken getRefreshToken(String refreshToken) {
String json = redisTemplate.opsForValue().get(getRefreshTokenKey(refreshToken));
if(StringUtils.isEmpty(json)) {
return null;
}
JSONObject jsonObj = JSON.parseObject(json);
String userJson = jsonObj.getString("openUser");
UserInfo user = JSON.parseObject(userJson, UserInfo.class);
String accessToken = jsonObj.getString("accessToken");
return new RefreshToken(accessToken, user);
}
@Override
public boolean checkAuthCode(String authCode) {
if(StringUtils.isEmpty(authCode)) {
return false;
}
return redisTemplate.hasKey(getCodeKey(authCode));
}
@Override
public OpenUser getUserByAuthCode(String authCode) {
String json = redisTemplate.opsForValue().get(getCodeKey(authCode));
if(StringUtils.isEmpty(json)) {
return null;
}
return JSON.parseObject(json, UserInfo.class);
}
@Override
public OpenUser getUserByAccessToken(String accessToken) {
String json = redisTemplate.opsForValue().get(getAccessTokenKey(accessToken));
if(StringUtils.isEmpty(json)) {
return null;
}
return JSON.parseObject(json, UserInfo.class);
}
@Override
public Map<String, String> getParam(OpenUser user) {
Map<String, String> map = new HashMap<>();
map.put("username", user.getUsername());
return map;
}
@Override
public OpenUser login(HttpServletRequest request) throws LoginErrorException {
// 这里应该先检查用户有没有登录,如果登录直接返回openUser
String username = request.getParameter("username");
String password = request.getParameter("password");
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
throw new LoginErrorException("用户名密码不能为空");
}
Query query = new Query();
query.eq("username", username)
.eq("password", password);
UserInfo userInfo = userInfoMapper.getByQuery(query);
if(userInfo == null) {
throw new LoginErrorException("用户名密码不正确");
}
return userInfo;
}
}

@ -0,0 +1,284 @@
package com.gitee.sop.sopauth.auth.impl;
import com.gitee.sop.sopauth.auth.AppIdManager;
import com.gitee.sop.sopauth.auth.FetchTokenParam;
import com.gitee.sop.sopauth.auth.FetchTokenResult;
import com.gitee.sop.sopauth.auth.OAuth2Config;
import com.gitee.sop.sopauth.auth.OAuth2Manager;
import com.gitee.sop.sopauth.auth.OAuth2Service;
import com.gitee.sop.sopauth.auth.OpenUser;
import com.gitee.sop.sopauth.auth.RefreshToken;
import com.gitee.sop.sopauth.auth.TokenPair;
import com.gitee.sop.sopauth.auth.exception.LoginErrorException;
import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.as.response.OAuthASResponse.OAuthTokenResponseBuilder;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* oauth2服务端默认实现
*
* @author tanghc
*/
@Service
@Slf4j
public class OAuth2ServiceImpl implements OAuth2Service {
private static final String TOKEN_TYPE = "Bearer";
private OAuthIssuer oauthIssuer = new OAuthIssuerImpl(new MD5Generator());
@Autowired
private OAuth2Manager oauth2Manager;
@Autowired
private AppIdManager appIdManager;
@Override
public OAuthResponse authorize(HttpServletRequest request, HttpServletResponse resp, OAuth2Config authConfig)
throws URISyntaxException, OAuthSystemException {
try {
// 构建OAuth 授权请求
OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
String clientId = oauthRequest.getClientId();
// 检查传入的客户端id是否正确
if (!appIdManager.isValidAppId(clientId)) {
return OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(OAuthError.TokenResponse.INVALID_CLIENT).buildJSONMessage();
}
// 如果用户没有登录,跳转到登陆页面
OpenUser user = null;
try {
user = oauth2Manager.login(request);
} catch (LoginErrorException e) {
log.error(e.getMessage(), e);
request.setAttribute("error", e.getMessage());
try {
request.getRequestDispatcher(authConfig.getOauth2LoginUri()).forward(request, resp);
return null;
} catch (Exception e1) {
log.error(e1.getMessage(), e1);
throw new RuntimeException(e1);
}
}
// 生成授权码
String authorizationCode = null;
// responseType目前仅支持CODE,另外还有TOKEN
String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
if (responseType.equals(ResponseType.CODE.toString())) {
OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
authorizationCode = oauthIssuerImpl.authorizationCode();
oauth2Manager.addAuthCode(authorizationCode, user);
}
// 进行OAuth响应构建
OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse.authorizationResponse(request,
HttpServletResponse.SC_FOUND);
// 设置授权码
builder.setCode(authorizationCode);
// 得到到客户端重定向地址
String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
// 构建响应
return builder.location(redirectURI).buildQueryMessage();
} catch (OAuthProblemException e) {
// 出错处理
String redirectUri = e.getRedirectUri();
if (OAuthUtils.isEmpty(redirectUri)) {
// 告诉客户端没有传入redirectUri直接报错
String error = "OAuth redirectUri needs to be provided by client!!!";
return OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
.error(OAuthProblemException.error(error)).location(redirectUri).buildQueryMessage();
} else {
// 返回错误消息(如?error=)
return OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND).error(e).location(redirectUri)
.buildQueryMessage();
}
}
}
@Override
public FetchTokenResult accessToken(FetchTokenParam fetchTokenParam, OAuth2Config authConfig) {
try {
// 这里需要检查client_id和client_secret,但是已经通过网关接口访问进来了
// 表示client_id和client_secret是合法的
// 检查验证类型,如果是第一次进来用code换取accessToken
String grant_type = fetchTokenParam.getGrant_type();
if (GrantType.AUTHORIZATION_CODE.toString().equals(grant_type)) {
String authCode = fetchTokenParam.getCode();
if (!oauth2Manager.checkAuthCode(authCode)) {
FetchTokenResult errorResult = new FetchTokenResult();
errorResult.setError(OAuthError.CodeResponse.INVALID_REQUEST);
errorResult.setError_description("invalid request");
return errorResult;
}
// 生成Access Token
OpenUser user = oauth2Manager.getUserByAuthCode(authCode);
if (user == null) {
throw OAuthProblemException.error("Can not found user by code.");
}
TokenPair tokenPair = this.createNewToken(user);
oauth2Manager.addAccessToken(tokenPair.getAccessToken(), tokenPair.getRefreshToken(), user);
// 生成OAuth响应
return this.buildTokenResult(tokenPair, user);
} else if (GrantType.REFRESH_TOKEN.toString().equals(grant_type)) {
// 用refreshToken来刷新accessToken
String refreshToken = fetchTokenParam.getRefresh_token();
if (StringUtils.isEmpty(refreshToken)) {
FetchTokenResult errorResult = new FetchTokenResult();
errorResult.setError(OAuthError.ResourceResponse.EXPIRED_TOKEN);
errorResult.setError_description("expired token");
return errorResult;
}
RefreshToken refreshTokenObj = oauth2Manager.getRefreshToken(refreshToken);
if (refreshTokenObj == null) {
FetchTokenResult errorResult = new FetchTokenResult();
errorResult.setError(OAuthError.ResourceResponse.INVALID_TOKEN);
errorResult.setError_description("invalid token");
return errorResult;
}
OpenUser user = refreshTokenObj.getOpenUser();
// 老的token对
TokenPair oldTokenPair = new TokenPair();
oldTokenPair.setAccessToken(refreshTokenObj.getAccessToken());
oldTokenPair.setRefreshToken(refreshToken);
// 创建一对新的accessToken和refreshToken
TokenPair newTokenPair = this.createRefreshToken(user, oldTokenPair);
this.afterRefreshToken(oldTokenPair, newTokenPair, user);
// 返回新的accessToken和refreshToken
return this.buildTokenResult(newTokenPair, user);
} else {
FetchTokenResult errorResult = new FetchTokenResult();
errorResult.setError(OAuthError.TokenResponse.INVALID_GRANT);
errorResult.setError_description("invalid grant");
return errorResult;
}
} catch (OAuthProblemException e) {
log.error(e.getMessage(), e);
FetchTokenResult errorResult = new FetchTokenResult();
errorResult.setError(e.getMessage());
errorResult.setError_description("invalid grant");
return errorResult;
}
}
/**
* 刷新token后续操作
*
* @param oldTokenPair 老的token
* @param newTokenPair 新的token
* @param user 用户
*/
protected void afterRefreshToken(TokenPair oldTokenPair, TokenPair newTokenPair, OpenUser user) {
// 保存token
oauth2Manager.addAccessToken(newTokenPair.getAccessToken(), newTokenPair.getRefreshToken(), user);
// 删除老的accessToken
oauth2Manager.removeAccessToken(oldTokenPair.getAccessToken());
// 删除老的refreshToken
oauth2Manager.removeRefreshToken(oldTokenPair.getRefreshToken());
}
/**
* 创建新的token
*
* @param user
* @return 返回新token
*/
protected TokenPair createNewToken(OpenUser user) {
return this.createDefaultTokenPair();
}
/**
* 返回刷新后token
*
* @param user 用户
* @param oldTokenPair 旧的token
* @return 返回新的token
*/
protected TokenPair createRefreshToken(OpenUser user, TokenPair oldTokenPair) {
return this.createDefaultTokenPair();
}
private TokenPair createDefaultTokenPair() {
TokenPair tokenPair = new TokenPair();
try {
String accessToken = oauthIssuer.accessToken();
String refreshToken = oauthIssuer.refreshToken();
tokenPair.setAccessToken(accessToken);
tokenPair.setRefreshToken(refreshToken);
return tokenPair;
} catch (OAuthSystemException e) {
throw new RuntimeException(e);
}
}
private FetchTokenResult buildTokenResult(TokenPair tokenPair, OpenUser user) {
OAuth2Config auth2Config = OAuth2Config.getInstance();
FetchTokenResult fetchTokenResult = new FetchTokenResult();
fetchTokenResult.setApp_auth_token(tokenPair.getAccessToken());
fetchTokenResult.setApp_refresh_token(tokenPair.getRefreshToken());
fetchTokenResult.setUser_id(user.getUserId());
fetchTokenResult.setExpires_in(auth2Config.getAccessTokenExpiresIn());
fetchTokenResult.setRe_expires_in(auth2Config.getRefreshTokenExpiresIn());
return fetchTokenResult;
}
private OAuthResponse buildAccessTokenResponse(TokenPair tokenPair, long expiresIn, OpenUser user) throws OAuthSystemException {
OAuthTokenResponseBuilder resp = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK);
Map<String, String> param = oauth2Manager.getParam(user);
if (param != null) {
Set<Entry<String, String>> entrySet = param.entrySet();
for (Entry<String, String> entry : entrySet) {
resp.setParam(entry.getKey(), entry.getValue());
}
}
return resp
.setAccessToken(tokenPair.getAccessToken())
.setRefreshToken(tokenPair.getRefreshToken())
.setTokenType(TOKEN_TYPE)
.setExpiresIn(String.valueOf(expiresIn))
.buildJSONMessage();
}
}

@ -0,0 +1,20 @@
package com.gitee.sop.sopauth.config;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* 使用支付宝开放平台功能
*
* @author tanghc
*/
@Configuration
public class OpenServiceConfig extends AlipayServiceConfiguration {
static {
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error");
}
}

@ -0,0 +1,54 @@
package com.gitee.sop.sopauth.controller;
import com.gitee.sop.sdk.client.OpenClient;
import com.gitee.sop.sdk.model.OpenAuthTokenAppModel;
import com.gitee.sop.sdk.request.OpenAuthTokenAppRequest;
import com.gitee.sop.sdk.response.OpenAuthTokenAppResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author tanghc
*/
@Controller
@Slf4j
public class CallbackController {
String url = "http://localhost:8081/api"; // zuul
String appId = "2019032617262200001";
// 支付宝私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
OpenClient openClient = new OpenClient(url, appId, privateKey);
/**
* 模拟开发者回调这里需要开发者自己实现.通过code换取token
* @return
*/
@GetMapping("oauth2callback")
@ResponseBody
public String callback(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
servletResponse.setCharacterEncoding("UTF-8");
String app_id = servletRequest.getParameter("app_id");
String code = servletRequest.getParameter("code");
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
model.setCode(code);
model.setGrant_type("authorization_code");
request.setBizModel(model);
// 根据code获取token
OpenAuthTokenAppResponse response = openClient.execute(request);
if (response.isSuccess()) {
// 成功拿到token,开发者在这里保存token信息
// 后续使用token进行接口访问
log.info("授权成功,body:{}", response.getBody());
}
return response.getBody();
}
}

@ -0,0 +1,85 @@
package com.gitee.sop.sopauth.controller;
import com.gitee.sop.servercommon.annotation.ApiMapping;
import com.gitee.sop.sopauth.auth.FetchTokenParam;
import com.gitee.sop.sopauth.auth.FetchTokenResult;
import com.gitee.sop.sopauth.auth.OAuth2Config;
import com.gitee.sop.sopauth.auth.OAuth2Service;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
/**
* 授权认证
*/
@Controller
@RequestMapping("oauth2")
public class OAuth2Controller {
@Autowired
private OAuth2Service oAuth2Service;
// 第一步:授权URL拼装
// https://localhost:8087/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback
@GetMapping("appToAppAuth")
public String appToAppAuth(HttpServletRequest request, ModelMap modelMap) {
String app_id = request.getParameter("app_id");
String redirect_uri = request.getParameter("redirect_uri");
modelMap.put("response_type", "code");
modelMap.put("client_id", app_id);
modelMap.put("redirect_uri", redirect_uri);
return "oauth2login";
}
/**
* 第二步点击登录到这里拿code
* oauth2认证获取code
* @param request
* @param resp
* @return 返回code
* @throws URISyntaxException
* @throws OAuthSystemException
*/
@RequestMapping("authorize")
public Object authorize(HttpServletRequest request, HttpServletResponse resp) throws URISyntaxException, OAuthSystemException {
OAuthResponse response = oAuth2Service.authorize(request, resp, OAuth2Config.getInstance());
if(response == null) {
return null;
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(response.getLocationUri()));
return new ResponseEntity<String>(headers, HttpStatus.valueOf(response.getResponseStatus()));
}
/**
* 第三步通过code获取token
* 或者通过refresh_token换取token
* @param param
* @return
*/
@ApiMapping("open.auth.token.app")
@ResponseBody
public FetchTokenResult fetchToken(FetchTokenParam param) {
FetchTokenResult fetchTokenResult = oAuth2Service.accessToken(param, OAuth2Config.getInstance());
return fetchTokenResult;
}
@RequestMapping("login")
public String oauth2login() {
return "oauth2login";
}
}

@ -0,0 +1,32 @@
package com.gitee.sop.sopauth.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 表名isv_info
* 备注isv信息表
*
* @author tanghc
*/
@Table(name = "isv_info")
@Data
public class IsvInfo {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段:id */
private Long id;
/** appKey, 数据库字段:app_key */
private String appKey;
/** 1启用,2禁用, 数据库字段:status */
private Byte status;
}

@ -0,0 +1,48 @@
package com.gitee.sop.sopauth.entity;
import com.gitee.sop.sopauth.auth.OpenUser;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名user_info
* 备注用户信息表
*
* @author tanghc
*/
@Table(name = "user_info")
@Data
public class UserInfo implements OpenUser {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段:id */
private Long id;
/** 用户名, 数据库字段:username */
private String username;
/** 密码, 数据库字段:password */
private String password;
/** 昵称, 数据库字段:nickname */
private String nickname;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
@Override
public String getUserId() {
return String.valueOf(id);
}
}

@ -0,0 +1,11 @@
package com.gitee.sop.sopauth.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.sopauth.entity.IsvInfo;
/**
* @author tanghc
*/
public interface IsvInfoMapper extends CrudMapper<IsvInfo, Long> {
}

@ -0,0 +1,11 @@
package com.gitee.sop.sopauth.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.sopauth.entity.UserInfo;
/**
* @author tanghc
*/
public interface UserInfoMapper extends CrudMapper<UserInfo, Long> {
}

@ -0,0 +1,35 @@
server:
port: 8087
# 注册中心
eureka:
port: 1111
host: localhost
client:
serviceUrl:
defaultZone: http://${eureka.host}:${eureka.port}/eureka/
spring:
application:
name: sop-auth
cloud:
zookeeper:
connect-string: localhost:2181
# 数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
username: root
password: root
thymeleaf:
cache: false
logging:
level:
com:
gitee: debug

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>用户登录</title>
</head>
<body>
<div th:if="${error}" style="color: red;"><span th:text="${error}"></span></div>
<form th:action="@{/oauth2/authorize}" method="post">
<input name="response_type" type="hidden" th:value="${response_type}"/>
<input name="client_id" type="hidden" th:value="${client_id}"/>
<input name="redirect_uri" type="hidden" th:value="${redirect_uri}"/>
用户名: <input type="text" name="username" value="zhangsan"/> <br/>
密码: <input type="password" name="password" value="123456"/> <br/>
<button type="submit">登录并授权</button>
</form>
</body>
</html>

@ -0,0 +1,40 @@
package com.gitee.sop.sopauth;
import com.gitee.sop.sdk.client.OpenClient;
import com.gitee.sop.sdk.model.OpenAuthTokenAppModel;
import com.gitee.sop.sdk.request.OpenAuthTokenAppRequest;
import com.gitee.sop.sdk.response.OpenAuthTokenAppResponse;
import junit.framework.TestCase;
import lombok.extern.slf4j.Slf4j;
/**
* @author tanghc
*/
@Slf4j
public class RefreshTokenTest extends TestCase {
String url = "http://localhost:8081/api"; // zuul
String appId = "2019032617262200001";
// 支付宝私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
OpenClient openClient = new OpenClient(url, appId, privateKey);
/**
* 根据refreshToken换取token
*/
public void testRefreshToken() {
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
model.setGrant_type("refresh_token");
model.setRefresh_token("856faf8d77d3b985c1073557ce6ea724");
request.setBizModel(model);
OpenAuthTokenAppResponse response = openClient.execute(request);
if (response.isSuccess()) {
// 成功拿到token,开发者在这里保存token信息
// 后续使用token进行接口访问
log.info("换取token成功,body:{}", response.getBody());
}
}
}

@ -0,0 +1,16 @@
package com.gitee.sop.sopauth;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SopAuthApplicationTests {
@Test
public void contextLoads() {
}
}

@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId> <artifactId>sop-service-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>

@ -29,7 +29,7 @@
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId> <artifactId>sop-service-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>

@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId> <artifactId>sop-service-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- eureka 服务发现 --> <!-- eureka 服务发现 -->
<dependency> <dependency>

@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId> <artifactId>sop-service-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>

@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId> <artifactId>sop-gateway-common</artifactId>
<version>1.5.0-SNAPSHOT</version> <version>1.6.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ --> <!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ -->

@ -0,0 +1,28 @@
package com.gitee.sop.sdk.model;
import lombok.Data;
/**
* Oauth2认证参数
* @author tanghc
*/
@Data
public class OpenAuthTokenAppModel {
/**
* 授权类型<br>
* 如果使用app_auth_code换取token则为authorization_code
* 如果使用refresh_token换取新的token则为refresh_token
*/
private String grant_type;
/**
* 授权码.与refresh_token二选一用户对应用授权后得到即第一步中开发者获取到的app_auth_code值
*/
private String code;
/**
* 刷新令牌.与code二选一可为空刷新令牌时使用
*/
private String refresh_token;
}

@ -0,0 +1,13 @@
package com.gitee.sop.sdk.request;
import com.gitee.sop.sdk.response.OpenAuthTokenAppResponse;
/**
* @author tanghc
*/
public class OpenAuthTokenAppRequest extends BaseRequest<OpenAuthTokenAppResponse> {
@Override
protected String method() {
return "open.auth.token.app";
}
}

@ -1,10 +1,12 @@
package com.gitee.sop.sdk.response; package com.gitee.sop.sdk.response;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date; import java.util.Date;
@Data @Data
@EqualsAndHashCode(callSuper = false)
public class GetStoryResponse extends BaseResponse { public class GetStoryResponse extends BaseResponse {
private Long id; private Long id;
private String name; private String name;

@ -0,0 +1,30 @@
package com.gitee.sop.sdk.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author tanghc
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class OpenAuthTokenAppResponse extends BaseResponse {
/**
* 授权令牌
*/
private String app_auth_token;
/**
* 用户id
*/
private String user_id;
/**
* 刷新令牌时使用
* 刷新令牌后我们会保证老的app_auth_token从刷新开始10分钟内可继续使用请及时替换为最新token
*/
private String app_refresh_token;
private String error;
private String error_description;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@ -0,0 +1,288 @@
<html data-find="_6">
<head>
<script src="https://www.googletagservices.com/activeview/js/current/osd.js?cb=%2Fr20100101"></script>
<meta charset="utf-8">
<title>应用授权</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="../../assets/lib/layui/css/layui.css" media="all">
<link rel="stylesheet" href="../../assets/css/global.css" media="all">
<link id="layuicss-skincodecss" rel="stylesheet" href="../../assets/lib/layui/css/modules/code.css" media="all">
<link id="layuicss-layer" rel="stylesheet" href="../../assets/lib/layui/css/modules/layer/default/layer.css?v=3.1.1"
media="all">
<style type="text/css">
.layui-table th {
white-space: nowrap !important;
}
h1 {font-size: 22px;margin-top: 20px;margin-bottom: 20px;font-weight: bold;}
.site-text p {color: rgba(0, 0, 0, .65);}
</style>
</head>
<body data-find="_5">
<div class="layui-header header header-doc" spring="">
<div class="layui-main">
<a class="logo" href="#" style="color: #ffffff">
开放平台
</a>
<ul class="layui-nav">
<li class="layui-nav-item layui-this">
<a href="doc.html">文档中心<!-- --></a>
</li>
<span class="layui-nav-bar" style="left: 162px; top: 55px; width: 0px; opacity: 0;"></span>
</ul>
</div>
</div>
<!-- 让IE8/9支持媒体查询,从而兼容栅格 -->
<!--[if lt IE 9]>
<script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script>
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<div class="layui-main site-inline">
<div class="site-tree">
<ul id="docItemTree" class="layui-tree">
<li><h2 class="docModule">第三方应用授权</h2></li>
<li class="site-tree-noicon layui-this">
<a href="#">
<cite>第三方应用授权</cite>
</a>
</li>
</ul>
</div>
<div class="site-content">
<h1>第三方应用授权</h1>
<div class="site-title">
<fieldset class="layui-elem-field layui-field-title site-title">
<legend><a name="use">概述</a></legend>
</fieldset>
</div>
<div class="site-text">
<p>1、用户对开发者进行应用授权后,开发者可以帮助用户完成相应的业务逻辑。</p>
<p>2、授权采用标准的OAuth 2.0流程。</p>
</div>
<h1>授权流程</h1>
<img src="../../assets/image/auth.png" style="width: 700px;"/>
<div class="site-title">
<fieldset class="layui-elem-field layui-field-title site-title">
<legend><a name="use">快速接入</a></legend>
</fieldset>
</div>
<div class="site-text">
<h1>第一步:应用授权URL拼装</h1>
<p><strong>拼接规则:</strong></p>
<p>http://openauth.yourdomain.com/oauth2/appToAppAuth?app_id=2019032617262200001&redirect_uri=http%3a%2f%2flocalhost%3a8087%2foauth2callback</p>
<p><strong>参数说明:</strong></p>
<table class="layui-table">
<thead>
<tr>
<th><strong>参数</strong></th>
<th><strong>参数名称</strong></th>
<th><strong>类型</strong></th>
<th><strong>必填</strong></th>
<th><strong>描述</strong></th>
<th><strong>范例</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>app_id</td>
<td>开发者应用的AppId</td>
<td>String</td>
<td></td>
<td>开发者应用的AppId</td>
<td>2015101400446982</td>
</tr>
<tr>
<td>redirect_uri</td>
<td>回调页面</td>
<td>String</td>
<td></td>
<td>参数需要UrlEncode</td>
<td>http%3A%2F%2Fexample.com</td>
</tr>
</tbody>
</table>
<h1>第二步:获取code</h1>
<p>授权成功后,客户端会跳转至开发者定义的回调页面(即redirect_uri参数对应的url),在回调页面请求中会带上当次授权的授权码code和开发者的app_id,示例如下:</p>
<p>http://devloper.domain.com/oauth2callback?app_id=2015101400446982&amp;code=ca34ea491e7146cc87d25fca24c4cD11</p>
<h1>第三步:使用code换取app_auth_token</h1>
<p>接口名称:open.auth.token.app</p>
<p>开发者通过code可以换取app_auth_token、授权用户的userId。</p>
<p><strong>注意:</strong>应用授权的code唯一,code使用一次后失效,有效期24小时; app_auth_token永久有效。</p>
<p><strong>请求参数说明</strong></p>
<table class="layui-table">
<thead>
<tr>
<th><strong>参数</strong></th>
<th><strong>参数名称</strong></th>
<th><strong>类型</strong></th>
<th><strong>必填</strong></th>
<th><strong>描述</strong></th>
<th><strong>范例</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>grant_type</td>
<td>授权类型</td>
<td>String</td>
<td></td>
<td>如果使用code换取token,则为authorization_code,如果使用refresh_token换取新的token,则为refresh_token</td>
<td>authorization_code</td>
</tr>
<tr>
<td>code</td>
<td>授权码</td>
<td>String</td>
<td></td>
<td>与refresh_token二选一,用户对应用授权后得到,即第一步中开发者获取到的code值</td>
<td>bf67d8d5ed754af297f72cc482287X62</td>
</tr>
<tr>
<td>refresh_token</td>
<td>刷新令牌</td>
<td>String</td>
<td></td>
<td>与code二选一,可为空,刷新令牌时使用</td>
<td>201510BB0c409dd5758b4d939d4008a525463X62</td>
</tr>
</tbody>
</table>
<p><strong>接口请求SDK示例</strong></p>
<pre class="layui-code">
@GetMapping("oauth2callback")
@ResponseBody
public String callback(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
String app_id = servletRequest.getParameter("app_id");
String code = servletRequest.getParameter("code");
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
model.setCode(code);
model.setGrant_type("authorization_code");
request.setBizModel(model);
// 根据code获取token
OpenAuthTokenAppResponse response = openClient.execute(request);
if (response.isSuccess()) {
// 成功拿到token,开发者在这里保存token信息
// 后续使用token进行接口访问
log.info("授权成功,body:{}", response.getBody());
}
return response.getBody();
}
</pre>
</div>
<p><strong>同步响应参数说明</strong></p>
<table class="layui-table">
<thead>
<tr>
<th><strong>参数</strong></th>
<th><strong>参数名称</strong></th>
<th><strong>类型</strong></th>
<th><strong>必填</strong></th>
<th><strong>描述</strong></th>
<th><strong>范例</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>app_auth_token</td>
<td>用户授权令牌</td>
<td>String</td>
<td></td>
<td>通过该令牌来帮助用户发起请求,完成业务</td>
<td>856faf8d77d3b985c1073557ce6ea724</td>
</tr>
<tr>
<td>user_id</td>
<td>授权用户的ID</td>
<td>String</td>
<td></td>
<td>授权者id</td>
<td>1</td>
</tr>
<tr>
<td>expires_in</td>
<td>令牌有效期</td>
<td>Long</td>
<td></td>
<td>负值表示永久有效</td>
<td>-1</td>
</tr>
<tr>
<td>re_expires_in</td>
<td>刷新令牌有效期</td>
<td>Long</td>
<td></td>
<td>负值表示永久有效</td>
<td>-3</td>
</tr>
<tr>
<td>app_refresh_token</td>
<td>刷新令牌时使用</td>
<td>String</td>
<td></td>
<td>刷新令牌后,我们会保证老的app_auth_token从刷新开始10分钟内可继续使用,请及时替换为最新token</td>
<td>88e68196d2359667f0dc8136e6c86803</td>
</tr>
</tbody>
</table>
<p><strong>同步响应结果示例</strong></p>
<pre class="layui-code"><code>{
"open_auth_token_app_response": {
"code": "10000",
"msg": "Success",
"app_auth_token": "88e68196d2359667f0dc8136e6c86803",
"app_refresh_token": "856faf8d77d3b985c1073557ce6ea724",
"expires_in": -1,
"re_expires_in": -3,
"user_id": "1"
},
"sign": "xxx"
}
</code></pre>
<p><strong>刷新token</strong></p>
<pre class="layui-code"><code>
OpenAuthTokenAppRequest request = new OpenAuthTokenAppRequest();
OpenAuthTokenAppModel model = new OpenAuthTokenAppModel();
model.setGrant_type("refresh_token");
model.setRefresh_token("856faf8d77d3b985c1073557ce6ea724");
request.setBizModel(model);
OpenAuthTokenAppResponse response = openClient.execute(request);
if (response.isSuccess()) {
// 成功拿到token,开发者在这里保存token信息
// 后续使用token进行接口访问
log.info("换取token成功,body:{}", response.getBody());
}
</code></pre>
</div>
</div><!-- layui-main end~ -->
<div class="layui-footer footer footer-doc">
<div class="layui-main">
<p>© 2019 <a href="https://gitee.com/durcframework/SOP" target="_blank">SOP</a> MIT license</p>
</div>
</div>
<script src="../../assets/lib/layui/layui.js" charset="utf-8"></script>
<script src="../../config/config.js" charset="utf-8"></script>
<script src="../../assets/lib/jquery/3.2.1/jquery.min.js" charset="utf-8"></script>
<script>
layui.use('code', function(){ //加载code模块
layui.code(); //引用code方法
});
</script>
</body>
</html>

@ -222,7 +222,7 @@
<td>String</td> <td>String</td>
<td></td> <td></td>
<td>40</td> <td>40</td>
<td>暂未实现</td> <td>详见<a href="auth.html" target="_blank">应用授权概述</a></td>
<td></td> <td></td>
</tr> </tr>

@ -16,6 +16,7 @@ DROP TABLE IF EXISTS `isv_info`;
DROP TABLE IF EXISTS `config_route_limit`; DROP TABLE IF EXISTS `config_route_limit`;
DROP TABLE IF EXISTS `config_route_base`; DROP TABLE IF EXISTS `config_route_base`;
DROP TABLE IF EXISTS `admin_user_info`; DROP TABLE IF EXISTS `admin_user_info`;
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `admin_user_info` ( CREATE TABLE `admin_user_info` (
@ -106,7 +107,16 @@ CREATE TABLE `perm_role_permission` (
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='角色权限表'; ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='角色权限表';
CREATE TABLE `user_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
`nickname` varchar(64) NOT NULL DEFAULT '' COMMENT '昵称',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_unamepwd` (`username`,`password`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS; SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS;
@ -191,7 +201,12 @@ INSERT INTO `perm_role_permission` (`id`, `role_code`, `route_id`, `gmt_create`,
ALTER TABLE `perm_role_permission` ENABLE KEYS; ALTER TABLE `perm_role_permission` ENABLE KEYS;
UNLOCK TABLES; UNLOCK TABLES;
LOCK TABLES `user_info` WRITE;
ALTER TABLE `user_info` DISABLE KEYS;
INSERT INTO `user_info` (`id`, `username`, `password`, `nickname`, `gmt_create`, `gmt_modified`) VALUES
(1,'zhangsan','123456','张三','2019-04-27 08:32:57','2019-04-27 08:32:57');
ALTER TABLE `user_info` ENABLE KEYS;
UNLOCK TABLES;
SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS; SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS;

Loading…
Cancel
Save