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