parent
6e36165c20
commit
467ec052eb
@ -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'); |
@ -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,3 @@ |
|||||||
|
spring: |
||||||
|
profiles: |
||||||
|
active: dev |
@ -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() { |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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"; |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
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&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> |
Loading…
Reference in new issue