Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:支持小红书商业平台 #229

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,36 @@ public String userInfo() {
public Class<? extends AuthDefaultRequest> getTargetClass() {
return null;
}
},

/**
* 小红书商业开放平台授权登录
*/
XHS {
@Override
public String authorize() {
return " https://ad-market.xiaohongshu.com/auth";
}

@Override
public String accessToken() {
return "https://adapi.xiaohongshu.com/api/open/oauth2/access_token";
}

@Override
public String userInfo() {
throw new UnsupportedOperationException("不支持获取用户信息 url");
}

@Override
public String refresh() {
return "https://adapi.xiaohongshu.com/api/open/oauth2/refresh_token";
}

@Override
public Class<? extends AuthDefaultRequest> getTargetClass() {
return AuthXiaohongshuMarketingRequest.class;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.zhyd.oauth.enums.scope;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* 小红书商业平台 OAuth 授权范围
*
* @author yangjiahao
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthXiaohongshuMarketingScope implements AuthScope {

/**
* {@code scope} 含义,以{@code description} 为准
*/
report_service("report_service", "获取账户报表信息", true),
ad_query("ad_query", "获取推广计划、推广单元、推广创意信息", false),
ad_manage("ad_manage", "创建&修改推广计划、推广单元、推广创意", false),
account_manage("account_manage", "账户管理", false);

private final String scope;
private final String description;
private final boolean isDefault;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package me.zhyd.oauth.request;

import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.scope.AuthXiaohongshuMarketingScope;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;

import java.util.HashMap;
import java.util.Map;

/**
* 小红书商业开放平台登录
* <p>
* 小红书商业开放平台
*
* @author yangjiahao
* @since 2024-10-08
*/
public class AuthXiaohongshuMarketingRequest extends AuthDefaultRequest {
public AuthXiaohongshuMarketingRequest(AuthConfig config) {
super(config, AuthDefaultSource.XHS);
}

public AuthXiaohongshuMarketingRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.XHS, authStateCache);
}

/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appId", config.getClientId())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthXiaohongshuMarketingScope.values())))
.queryParam("redirectUri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}

@Override
public AuthToken getAccessToken(AuthCallback authCallback) {

Map<String, String> params = new HashMap<>(7);
params.put("app_id", config.getClientId());
params.put("secret", config.getClientSecret());
params.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
JSONObject object = JSONObject.parseObject(response);

this.checkResponse(object);

return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("access_token_expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.build();
}

@Override
public AuthUser getUserInfo(AuthToken authToken) {
throw new UnsupportedOperationException("不支持获取用户信息 url");
}

@Override
public AuthResponse<AuthToken> refresh(AuthToken oldToken) {
Map<String, String> form = new HashMap<>(7);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("refresh_token", oldToken.getRefreshToken());

String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);

this.checkResponse(object);

return AuthResponse.<AuthToken>builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.expireIn(object.getIntValue("expires_in"))
.build())
.build();
}


/**
* 检查响应内容是否正确
*
* @param
*/
private void checkResponse(JSONObject object) {
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
}


}