Skip to content

Commit

Permalink
feat(#14): redirect to mp-qr
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff-Tian committed Sep 5, 2023
1 parent ebbd2e7 commit 9589c44
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services-social-weixin</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
<name>Keycloak Services Social WeiXin</name>
<description/>
<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.social.weixin.egress.wechat.mp.WechatMpApi;
import org.keycloak.social.weixin.egress.wechat.mp.models.ActionInfo;
import org.keycloak.social.weixin.egress.wechat.mp.models.Scene;
import org.keycloak.social.weixin.egress.wechat.mp.models.TicketRequest;
import org.keycloak.social.weixin.helpers.UserAgentHelper;
import org.keycloak.social.weixin.helpers.WMPHelper;

Expand Down Expand Up @@ -234,12 +238,19 @@ protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {

return uriBuilder;
} else {
// 未启用开放平台,且未配置自定义登录页面,则返回一个 html 页面,展示带参二维码
uriBuilder = UriBuilder.fromUri(config.getAuthorizationUrl());
uriBuilder.queryParam(OAUTH2_PARAMETER_SCOPE, config.getDefaultScope())
.queryParam(OAUTH2_PARAMETER_STATE, request.getState().getEncoded())
.queryParam(OAUTH2_PARAMETER_CLIENT_ID, config.getClientId())
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
logger.info("未启用开放平台,且未配置自定义登录页面,则返回一个 html 页面,展示带参二维码");
uriBuilder = UriBuilder.fromUri("/realms/" + request.getRealm().getName() + "/QrCodeResourceProviderFactory/mp-qr");

var wechatApi = new WechatMpApi(
config.getConfig().get(WECHAT_MP_APP_ID),
config.getConfig().get(WECHAT_MP_APP_SECRET),
session
);

var ticketUrl = wechatApi.createTmpQrCode(new TicketRequest(2592000, "QR_STR_SCENE", new ActionInfo(new Scene("1")))).url;
logger.info("ticketUrl = " + ticketUrl);

uriBuilder.queryParam("ticket-url", ticketUrl);
}
} else {
uriBuilder = UriBuilder.fromUri(config.getAuthorizationUrl());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.keycloak.social.weixin.egress.wechat.mp;

import lombok.SneakyThrows;
import org.jboss.logging.Logger;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.models.KeycloakSession;
import org.keycloak.social.weixin.egress.wechat.mp.models.AccessTokenResponse;
import org.keycloak.social.weixin.egress.wechat.mp.models.TicketRequest;
import org.keycloak.social.weixin.egress.wechat.mp.models.TicketResponse;


public class WechatMpApi {
private static final Logger logger = Logger.getLogger(WechatMpApi.class);
private final String appSecret;
private final String appId;
protected final KeycloakSession session;

public WechatMpApi(String appId, String appSecret, KeycloakSession session) {
this.appId = appId;
this.appSecret = appSecret;
this.session = session;
}

@SneakyThrows
public AccessTokenResponse getAccessToken(String appId, String appSecret) {
logger.info(String.format("getAccessToken by %s%n%s%n", appId, appSecret));
var res =
SimpleHttp.doGet(String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
"&appid=%s&secret=%s", appId, appSecret),
session).asJson(AccessTokenResponse.class);

logger.info(String.format("res is %s%n", res));

return res;
}

@SneakyThrows
public TicketResponse createTmpQrCode(TicketRequest ticketRequest) {
logger.info(String.format("createTmpQrCode by %s%n", ticketRequest));

var res = SimpleHttp.doPost("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + this.getAccessToken(appId, appSecret).access_token,
session).json(ticketRequest).asJson(TicketResponse.class);

logger.info(String.format("res is %s%n", res));

return res;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.keycloak.social.weixin.egress.wechat.mp;
package org.keycloak.social.weixin.egress.wechat.mp.models;

public class AccessTokenRequestBody {
public String grant_type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.keycloak.social.weixin.egress.wechat.mp.models;

public class AccessTokenResponse {
public String access_token;
public String expires_in;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.keycloak.social.weixin.egress.wechat.mp.models;

public class ActionInfo {
public Scene scene;

public ActionInfo(Scene scene) {
this.scene = scene;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.keycloak.social.weixin.egress.wechat.mp.models;

public class Scene {
public String scene_str;

public Scene(String scene_str) {
this.scene_str = scene_str;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.keycloak.social.weixin.egress.wechat.mp.models;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class TicketRequest {
public Number expire_seconds;
public String action_name;
public ActionInfo action_info;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.keycloak.social.weixin.egress.wechat.mp.models;

public class TicketResponse {
public String ticket;
public Number expire_seconds;
public String url;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.RequiredArgsConstructor;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.social.weixin.egress.wechat.mp.WechatMpApi;
import org.keycloak.social.weixin.egress.wechat.mp.models.ActionInfo;
import org.keycloak.social.weixin.egress.wechat.mp.models.Scene;
import org.keycloak.social.weixin.egress.wechat.mp.models.TicketRequest;

import java.util.Map;

Expand Down Expand Up @@ -36,9 +41,9 @@ public Response helloAnonymous() {
}

@GET
@Path("mp-qr-url")
@Path("mp-qr")
@Produces(MediaType.TEXT_HTML)
public Response mpQrUrl() {
public Response mpQrUrl(@QueryParam("ticket-url") String ticketUrl) {
logger.info("展示一个 HTML 页面,该页面使用 React 展示一个组件,它调用一个后端 API,得到一个带参二维码 URL,并将该 URL 作为 img 的 src 属性值");

String htmlContent = "<!DOCTYPE html>\n" +
Expand All @@ -48,21 +53,8 @@ public Response mpQrUrl() {
"</head>\n" +
"<body>\n" +
" <div id=\"qrCodeContainer\">\n" +
" Loading QR Code...\n" +
" <img src=\"" + ticketUrl + "\" alt=\"QR Code\">\n" +
" </div>\n" +
" <script>\n" +
" // 使用JavaScript获取后端API返回的二维码URL\n" +
" fetch('/api/get-qr-code-url')\n" +
" .then(response => response.json())\n" +
" .then(data => {\n" +
" const qrCodeUrl = data.qrCodeUrl;\n" +
" const qrCodeContainer = document.getElementById('qrCodeContainer');\n" +
" qrCodeContainer.innerHTML = `<img src=\"${qrCodeUrl}\" alt=\"QR Code\">`;\n" +
" })\n" +
" .catch(error => {\n" +
" console.error('Error fetching QR code URL:', error);\n" +
" });\n" +
" </script>\n" +
"</body>\n" +
"</html>";

Expand Down

0 comments on commit 9589c44

Please sign in to comment.