Commit 13e594d6 authored by jiatao's avatar jiatao

完善sso.json文件

parent bf7142c4
CAS_RELAY_PORT=18080
CAS_SERVICE_URL=https://nercwbs-lms.xidian.edu.cn/orgsso
CAS_SERVER_URL_PREFIX=https://ids.xidian.edu.cn/authserver
CAS_SERVICE_VALIDATE_PATH=/serviceValidate
CAS_LOGIN_PATH=/login
CAS_LOGOUT_PATH=/logout
MINGDAO_ENABLED=true
MINGDAO_REFRESH_ENABLED=true
MINGDAO_REFRESH_INTERVAL_MS=600000
MINGDAO_API_URL=https://nercwbs-lms.xidian.edu.cn/api/v2/open/worksheet/getFilterRows
MINGDAO_APP_KEY=3ed6154939e6aa92
MINGDAO_SIGN= MDM0ZWU1YWM4NzM0ODljZTZiNDJlYmUwYzRmODU4OTNlN2ZkYzIwMjI1ZjBhNzIwZjA4OWU3YzQ1NTdiNGM0Mg==
OAUTH_CLIENT_ID=1506958348348624896
OAUTH_CLIENT_SECRET=19E483DC4D272E4JD5KD
OAUTH_REDIRECT_URI=https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2
OAUTH_AUTHORIZATION_URI=https://ids.xidian.edu.cn/authserver/oauth2.0/authorize
OAUTH_TOKEN_URI=https://ids.xidian.edu.cn/authserver/oauth2.0/accessToken
OAUTH_USER_INFO_URI=https://ids.xidian.edu.cn/authserver/oauthApi/user/profile
OAUTH_USER_MOBILE_URI=https://ids.xidian.edu.cn/authserver/oauthApi/user/getMobile
WECOM_RELAY_ENABLED=true
WECOM_APP_ID=200260525100013951
WECOM_APP_SECRET=8ekpsdgmb9eh3m9w1gf3s1nm5ft2b3ne
WECOM_PLATFORM_DOMAIN=xxcapp.xidian.edu.cn
WECOM_CORP_ID=
WECOM_AGENT_ID=
WECOM_CORP_SECRET=
WECOM_REDIRECT_URI=https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2
WECOM_AUTHORIZATION_URI=https://xxcapp.xidian.edu.cn/uc/api/oauth/index
WECOM_AUTHORIZATION_APP_ID_PARAM=appid
WECOM_AUTHORIZATION_REDIRECT_URI_PARAM=redirect
WECOM_AUTHORIZATION_RESPONSE_TYPE_PARAM=
WECOM_AUTHORIZATION_SCOPE_PARAM=
WECOM_AUTHORIZATION_STATE_PARAM=state
WECOM_AUTHORIZATION_QRCODE_PARAM=qrcode
WECOM_AUTHORIZATION_QRCODE_VALUE=1
WECOM_ACCESS_TOKEN_URI=https://qyapi.weixin.qq.com/cgi-bin/gettoken
WECOM_CODE_USER_INFO_URI=https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo
WECOM_USER_DETAIL_URI=https://qyapi.weixin.qq.com/cgi-bin/user/get
WECOM_SCOPE=snsapi_base
WECOM_MOCK_ENABLED=false
WECOM_MOCK_WORK_NO=20240001
WECOM_MOCK_NAME=Mock User
WECOM_MOCK_EMAIL=20240001@xidian.edu.cn
WECOM_MOCK_MOBILE=13800000000
......@@ -19,3 +19,37 @@ OAUTH_AUTHORIZATION_URI=https://ids.xidian.edu.cn/authserver/oauth2.0/authorize
OAUTH_TOKEN_URI=https://ids.xidian.edu.cn/authserver/oauth2.0/accessToken
OAUTH_USER_INFO_URI=https://ids.xidian.edu.cn/authserver/oauthApi/user/profile
OAUTH_USER_MOBILE_URI=https://ids.xidian.edu.cn/authserver/oauthApi/user/getMobile
WECOM_RELAY_ENABLED=false
WECOM_APP_ID=200260525100013951
WECOM_APP_SECRET=
WECOM_PLATFORM_DOMAIN=xxcapp.xidian.edu.cn
WECOM_CORP_ID=
WECOM_AGENT_ID=
WECOM_CORP_SECRET=
WECOM_REDIRECT_URI=https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2
WECOM_ALLOWED_REDIRECT_HOST=nercwbs-lms.xidian.edu.cn
WECOM_AUTHORIZATION_URI=https://xxcapp.xidian.edu.cn/uc/api/oauth/index
WECOM_AUTHORIZATION_APP_ID_PARAM=appid
WECOM_AUTHORIZATION_REDIRECT_URI_PARAM=redirect
WECOM_AUTHORIZATION_RESPONSE_TYPE_PARAM=
WECOM_AUTHORIZATION_SCOPE_PARAM=
WECOM_AUTHORIZATION_STATE_PARAM=state
WECOM_AUTHORIZATION_QRCODE_PARAM=qrcode
WECOM_AUTHORIZATION_QRCODE_VALUE=1
WECOM_CODE_EXCHANGE_URI=https://xxcapp.xidian.edu.cn/api/third/get-token
WECOM_TOKEN_USER_INFO_URI=https://xxcapp.xidian.edu.cn/uc/api/oauth/user-by-code
WECOM_CODE_PARAM=code
WECOM_CODE_EXCHANGE_APP_ID_PARAM=appid
WECOM_CODE_EXCHANGE_APP_SECRET_PARAM=appsecret
WECOM_TOKEN_USER_INFO_TOKEN_PARAM=access_token
WECOM_PLATFORM_ACCESS_TOKEN_SKEW_SECONDS=300
WECOM_ACCESS_TOKEN_URI=
WECOM_CODE_USER_INFO_URI=
WECOM_USER_DETAIL_URI=
WECOM_SCOPE=snsapi_base
WECOM_MOCK_ENABLED=false
WECOM_MOCK_WORK_NO=20240001
WECOM_MOCK_NAME=MockUser
WECOM_MOCK_EMAIL=20240001@xidian.edu.cn
WECOM_MOCK_MOBILE=13800000000
FROM openjdk:8
FROM eclipse-temurin:8-jre
WORKDIR /app
......
......@@ -14,6 +14,17 @@ services:
MINGDAO_REFRESH_INTERVAL_MS: "${MINGDAO_REFRESH_INTERVAL_MS:-600000}"
MINGDAO_APP_KEY: "${MINGDAO_APP_KEY}"
MINGDAO_SIGN: "${MINGDAO_SIGN}"
WECOM_RELAY_ENABLED: "${WECOM_RELAY_ENABLED:-false}"
WECOM_APP_ID: "${WECOM_APP_ID:-200260525100013951}"
WECOM_APP_SECRET: "${WECOM_APP_SECRET:-}"
WECOM_PLATFORM_DOMAIN: "${WECOM_PLATFORM_DOMAIN:-xxcapp.xidian.edu.cn}"
WECOM_REDIRECT_URI: "${WECOM_REDIRECT_URI:-https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2}"
WECOM_ALLOWED_REDIRECT_HOST: "${WECOM_ALLOWED_REDIRECT_HOST:-nercwbs-lms.xidian.edu.cn}"
WECOM_AUTHORIZATION_URI: "${WECOM_AUTHORIZATION_URI:-https://xxcapp.xidian.edu.cn/uc/api/oauth/index}"
WECOM_AUTHORIZATION_APP_ID_PARAM: "${WECOM_AUTHORIZATION_APP_ID_PARAM:-appid}"
WECOM_AUTHORIZATION_REDIRECT_URI_PARAM: "${WECOM_AUTHORIZATION_REDIRECT_URI_PARAM:-redirect}"
WECOM_AUTHORIZATION_QRCODE_PARAM: "${WECOM_AUTHORIZATION_QRCODE_PARAM:-qrcode}"
WECOM_AUTHORIZATION_QRCODE_VALUE: "${WECOM_AUTHORIZATION_QRCODE_VALUE:-1}"
JAVA_OPTS: >-
-Dcas.service=${CAS_SERVICE_URL}
-Dcas.server-url-prefix=${CAS_SERVER_URL_PREFIX:-https://ids.xidian.edu.cn/authserver}
......@@ -28,3 +39,15 @@ services:
-Doauth-relay.token-uri=${OAUTH_TOKEN_URI:-https://ids.xidian.edu.cn/authserver/oauth2.0/accessToken}
-Doauth-relay.user-info-uri=${OAUTH_USER_INFO_URI:-https://ids.xidian.edu.cn/authserver/oauthApi/user/profile}
-Doauth-relay.user-mobile-uri=${OAUTH_USER_MOBILE_URI:-https://ids.xidian.edu.cn/authserver/oauthApi/user/getMobile}
-Dwecom-relay.code-exchange-uri=${WECOM_CODE_EXCHANGE_URI:-https://xxcapp.xidian.edu.cn/api/third/get-token}
-Dwecom-relay.token-user-info-uri=${WECOM_TOKEN_USER_INFO_URI:-https://xxcapp.xidian.edu.cn/uc/api/oauth/user-by-code}
-Dwecom-relay.code-param=${WECOM_CODE_PARAM:-code}
-Dwecom-relay.code-exchange-app-id-param=${WECOM_CODE_EXCHANGE_APP_ID_PARAM:-appid}
-Dwecom-relay.code-exchange-app-secret-param=${WECOM_CODE_EXCHANGE_APP_SECRET_PARAM:-appsecret}
-Dwecom-relay.token-user-info-token-param=${WECOM_TOKEN_USER_INFO_TOKEN_PARAM:-access_token}
-Dwecom-relay.platform-access-token-skew-seconds=${WECOM_PLATFORM_ACCESS_TOKEN_SKEW_SECONDS:-300}
-Dwecom-relay.allowed-redirect-host=${WECOM_ALLOWED_REDIRECT_HOST:-nercwbs-lms.xidian.edu.cn}
-Dwecom-relay.access-token-uri=${WECOM_ACCESS_TOKEN_URI:-}
-Dwecom-relay.code-user-info-uri=${WECOM_CODE_USER_INFO_URI:-}
-Dwecom-relay.user-detail-uri=${WECOM_USER_DETAIL_URI:-}
-Dwecom-relay.scope=${WECOM_SCOPE:-snsapi_base}
......@@ -7,7 +7,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableConfigurationProperties({CasRelayProperties.class, MingdaoProperties.class, OAuthRelayProperties.class})
@EnableConfigurationProperties({CasRelayProperties.class, MingdaoProperties.class, OAuthRelayProperties.class,
WeComRelayProperties.class})
public class CasRelayApplication {
public static void main(String[] args) {
......
package com.jd.easywalk.casrelay;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
public class DotenvPropertySourceInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger log = LoggerFactory.getLogger(DotenvPropertySourceInitializer.class);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Properties properties = new Properties();
try (InputStream inputStream = new FileInputStream(".env")) {
properties.load(inputStream);
} catch (Exception ex) {
log.info("Local .env file not loaded: {}", ex.getMessage());
return;
}
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.getPropertySources().addLast(new PropertiesPropertySource("localDotenv", properties));
log.info("Local .env file loaded");
}
}
......@@ -26,6 +26,7 @@ public class TeacherDirectoryService {
private final MingdaoProperties properties;
private final RestTemplate restTemplate;
private final Map<String, TeacherInfo> teachersByWorkNo = new ConcurrentHashMap<String, TeacherInfo>();
private final Map<String, TeacherInfo> teachersByMobile = new ConcurrentHashMap<String, TeacherInfo>();
public TeacherDirectoryService(MingdaoProperties properties, RestTemplateBuilder restTemplateBuilder) {
this.properties = properties;
......@@ -45,6 +46,7 @@ public class TeacherDirectoryService {
validateConfig();
Map<String, TeacherInfo> loaded = new HashMap<String, TeacherInfo>();
Map<String, TeacherInfo> loadedByMobile = new HashMap<String, TeacherInfo>();
int pageIndex = 1;
int pageSize = Math.max(properties.getPageSize(), 1);
while (true) {
......@@ -58,6 +60,9 @@ public class TeacherDirectoryService {
TeacherInfo teacher = toTeacherInfo(row);
if (teacher != null) {
loaded.put(teacher.getWorkNo(), teacher);
if (StringUtils.hasText(teacher.getMobile())) {
loadedByMobile.put(teacher.getMobile().trim(), teacher);
}
}
}
......@@ -69,6 +74,8 @@ public class TeacherDirectoryService {
teachersByWorkNo.clear();
teachersByWorkNo.putAll(loaded);
teachersByMobile.clear();
teachersByMobile.putAll(loadedByMobile);
log.info("Loaded {} teachers from Mingdao worksheet", teachersByWorkNo.size());
logTeacherDetails();
}
......@@ -80,6 +87,13 @@ public class TeacherDirectoryService {
return teachersByWorkNo.get(workNo.trim());
}
public TeacherInfo findByMobile(String mobile) {
if (!StringUtils.hasText(mobile)) {
return null;
}
return teachersByMobile.get(mobile.trim());
}
public int size() {
return teachersByWorkNo.size();
}
......
package com.jd.easywalk.casrelay;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;
@RestController
@RequestMapping("/wecom/oauth2")
public class WeComRelayController {
private static final Logger log = LoggerFactory.getLogger(WeComRelayController.class);
private final WeComRelayService weComRelayService;
public WeComRelayController(WeComRelayService weComRelayService) {
this.weComRelayService = weComRelayService;
}
@GetMapping({"/authorize", "/login"})
public RedirectView authorize(@RequestParam Map<String, String> params) {
String target = weComRelayService.buildAuthorizationRedirect(params);
log.info("WeCom OAuth relay authorize request: redirectUri={}, state={}, target={}",
params.get("redirect_uri"), params.get("state"), target);
RedirectView redirectView = new RedirectView(target);
redirectView.setExpandUriTemplateVariables(false);
return redirectView;
}
@GetMapping("/mock/authorize")
public RedirectView mockAuthorize(@RequestParam Map<String, String> params) {
String target = weComRelayService.buildMockAuthorizationRedirect(params);
log.info("WeCom OAuth relay mock authorize request: redirectUri={}, state={}, target={}",
params.get("redirect_uri"), params.get("state"), target);
RedirectView redirectView = new RedirectView(target);
redirectView.setExpandUriTemplateVariables(false);
return redirectView;
}
@PostMapping({"/access_token", "/token"})
public ResponseEntity<Map<String, Object>> accessToken(@RequestParam Map<String, String> params) {
log.info("WeCom OAuth relay token request received: grantType={}, hasCode={}, redirectUri={}",
params.get("grant_type"), StringUtils.hasText(params.get("code")), params.get("redirect_uri"));
Map<String, Object> body = weComRelayService.exchangeToken(params);
log.info("WeCom OAuth relay token response generated: keys={}, hasAccessToken={}, error={}",
body.keySet(), body.containsKey("access_token"), body.get("error_description"));
return ResponseEntity.ok(body);
}
@GetMapping({"/userinfo", "/user_info"})
public ResponseEntity<Map<String, Object>> userInfo(
@RequestParam(required = false) String token,
@RequestParam(required = false) String access_token) {
String realToken = StringUtils.hasText(token) ? token : access_token;
log.info("WeCom OAuth relay Mingdao userInfo request received: hasToken={}, hasAccessToken={}, token={}",
StringUtils.hasText(token), StringUtils.hasText(access_token), weComRelayService.maskForLog(realToken));
return ResponseEntity.ok(weComRelayService.userInfo(realToken));
}
}
package com.jd.easywalk.casrelay;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "wecom-relay")
public class WeComRelayProperties {
private boolean enabled = false;
private String appId = "";
private String appSecret = "";
private String platformDomain = "";
private String corpId = "";
private String agentId = "";
private String corpSecret = "";
private String authorizationUri = "https://xxcapp.xidian.edu.cn/uc/api/oauth/index";
private String codeExchangeUri = "https://xxcapp.xidian.edu.cn/api/third/get-token";
private String tokenUserInfoUri = "https://xxcapp.xidian.edu.cn/uc/api/oauth/user-by-code";
private String accessTokenUri = "";
private String codeUserInfoUri = "";
private String userDetailUri = "";
private String codeParam = "code";
private String codeExchangeAppIdParam = "appid";
private String codeExchangeAppSecretParam = "appsecret";
private String tokenUserInfoTokenParam = "access_token";
private int platformAccessTokenSkewSeconds = 300;
private String authorizationAppIdParam = "appid";
private String authorizationRedirectUriParam = "redirect";
private String authorizationResponseTypeParam = "";
private String authorizationScopeParam = "";
private String authorizationStateParam = "state";
private String authorizationQrcodeParam = "qrcode";
private String authorizationQrcodeValue = "1";
private String redirectUri = "";
private String allowedRedirectHost = "nercwbs-lms.xidian.edu.cn";
private String scope = "snsapi_base";
private int tokenExpiresIn = 7200;
private int corpAccessTokenSkewSeconds = 300;
private String emailDomain = "xidian.edu.cn";
private boolean synthesizeEmail = true;
private boolean appendWechatRedirect = true;
private int connectTimeoutMs = 5000;
private int readTimeoutMs = 10000;
private boolean mockEnabled = false;
private String mockWorkNo = "20240001";
private String mockName = "MockUser";
private String mockEmail = "20240001@xidian.edu.cn";
private String mockMobile = "13800000000";
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getPlatformDomain() {
return platformDomain;
}
public void setPlatformDomain(String platformDomain) {
this.platformDomain = platformDomain;
}
public String getCorpId() {
return corpId;
}
public void setCorpId(String corpId) {
this.corpId = corpId;
}
public String getAgentId() {
return agentId;
}
public void setAgentId(String agentId) {
this.agentId = agentId;
}
public String getCorpSecret() {
return corpSecret;
}
public void setCorpSecret(String corpSecret) {
this.corpSecret = corpSecret;
}
public String getAuthorizationUri() {
return authorizationUri;
}
public void setAuthorizationUri(String authorizationUri) {
this.authorizationUri = authorizationUri;
}
public String getAuthorizationAppIdParam() {
return authorizationAppIdParam;
}
public void setAuthorizationAppIdParam(String authorizationAppIdParam) {
this.authorizationAppIdParam = authorizationAppIdParam;
}
public String getAuthorizationRedirectUriParam() {
return authorizationRedirectUriParam;
}
public void setAuthorizationRedirectUriParam(String authorizationRedirectUriParam) {
this.authorizationRedirectUriParam = authorizationRedirectUriParam;
}
public String getAuthorizationResponseTypeParam() {
return authorizationResponseTypeParam;
}
public void setAuthorizationResponseTypeParam(String authorizationResponseTypeParam) {
this.authorizationResponseTypeParam = authorizationResponseTypeParam;
}
public String getAuthorizationScopeParam() {
return authorizationScopeParam;
}
public void setAuthorizationScopeParam(String authorizationScopeParam) {
this.authorizationScopeParam = authorizationScopeParam;
}
public String getAuthorizationStateParam() {
return authorizationStateParam;
}
public void setAuthorizationStateParam(String authorizationStateParam) {
this.authorizationStateParam = authorizationStateParam;
}
public String getAuthorizationQrcodeParam() {
return authorizationQrcodeParam;
}
public void setAuthorizationQrcodeParam(String authorizationQrcodeParam) {
this.authorizationQrcodeParam = authorizationQrcodeParam;
}
public String getAuthorizationQrcodeValue() {
return authorizationQrcodeValue;
}
public void setAuthorizationQrcodeValue(String authorizationQrcodeValue) {
this.authorizationQrcodeValue = authorizationQrcodeValue;
}
public String getAccessTokenUri() {
return accessTokenUri;
}
public void setAccessTokenUri(String accessTokenUri) {
this.accessTokenUri = accessTokenUri;
}
public String getCodeExchangeUri() {
return codeExchangeUri;
}
public void setCodeExchangeUri(String codeExchangeUri) {
this.codeExchangeUri = codeExchangeUri;
}
public String getTokenUserInfoUri() {
return tokenUserInfoUri;
}
public void setTokenUserInfoUri(String tokenUserInfoUri) {
this.tokenUserInfoUri = tokenUserInfoUri;
}
public String getCodeParam() {
return codeParam;
}
public void setCodeParam(String codeParam) {
this.codeParam = codeParam;
}
public String getCodeExchangeAppIdParam() {
return codeExchangeAppIdParam;
}
public void setCodeExchangeAppIdParam(String codeExchangeAppIdParam) {
this.codeExchangeAppIdParam = codeExchangeAppIdParam;
}
public String getCodeExchangeAppSecretParam() {
return codeExchangeAppSecretParam;
}
public void setCodeExchangeAppSecretParam(String codeExchangeAppSecretParam) {
this.codeExchangeAppSecretParam = codeExchangeAppSecretParam;
}
public String getTokenUserInfoTokenParam() {
return tokenUserInfoTokenParam;
}
public void setTokenUserInfoTokenParam(String tokenUserInfoTokenParam) {
this.tokenUserInfoTokenParam = tokenUserInfoTokenParam;
}
public int getPlatformAccessTokenSkewSeconds() {
return platformAccessTokenSkewSeconds;
}
public void setPlatformAccessTokenSkewSeconds(int platformAccessTokenSkewSeconds) {
this.platformAccessTokenSkewSeconds = platformAccessTokenSkewSeconds;
}
public String getCodeUserInfoUri() {
return codeUserInfoUri;
}
public void setCodeUserInfoUri(String codeUserInfoUri) {
this.codeUserInfoUri = codeUserInfoUri;
}
public String getUserDetailUri() {
return userDetailUri;
}
public void setUserDetailUri(String userDetailUri) {
this.userDetailUri = userDetailUri;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public String getAllowedRedirectHost() {
return allowedRedirectHost;
}
public void setAllowedRedirectHost(String allowedRedirectHost) {
this.allowedRedirectHost = allowedRedirectHost;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public int getTokenExpiresIn() {
return tokenExpiresIn;
}
public void setTokenExpiresIn(int tokenExpiresIn) {
this.tokenExpiresIn = tokenExpiresIn;
}
public int getCorpAccessTokenSkewSeconds() {
return corpAccessTokenSkewSeconds;
}
public void setCorpAccessTokenSkewSeconds(int corpAccessTokenSkewSeconds) {
this.corpAccessTokenSkewSeconds = corpAccessTokenSkewSeconds;
}
public String getEmailDomain() {
return emailDomain;
}
public void setEmailDomain(String emailDomain) {
this.emailDomain = emailDomain;
}
public boolean isSynthesizeEmail() {
return synthesizeEmail;
}
public void setSynthesizeEmail(boolean synthesizeEmail) {
this.synthesizeEmail = synthesizeEmail;
}
public boolean isAppendWechatRedirect() {
return appendWechatRedirect;
}
public void setAppendWechatRedirect(boolean appendWechatRedirect) {
this.appendWechatRedirect = appendWechatRedirect;
}
public int getConnectTimeoutMs() {
return connectTimeoutMs;
}
public void setConnectTimeoutMs(int connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
}
public int getReadTimeoutMs() {
return readTimeoutMs;
}
public void setReadTimeoutMs(int readTimeoutMs) {
this.readTimeoutMs = readTimeoutMs;
}
public boolean isMockEnabled() {
return mockEnabled;
}
public void setMockEnabled(boolean mockEnabled) {
this.mockEnabled = mockEnabled;
}
public String getMockWorkNo() {
return mockWorkNo;
}
public void setMockWorkNo(String mockWorkNo) {
this.mockWorkNo = mockWorkNo;
}
public String getMockName() {
return mockName;
}
public void setMockName(String mockName) {
this.mockName = mockName;
}
public String getMockEmail() {
return mockEmail;
}
public void setMockEmail(String mockEmail) {
this.mockEmail = mockEmail;
}
public String getMockMobile() {
return mockMobile;
}
public void setMockMobile(String mockMobile) {
this.mockMobile = mockMobile;
}
}
package com.jd.easywalk.casrelay;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@Service
public class WeComRelayService {
private static final Logger log = LoggerFactory.getLogger(WeComRelayService.class);
private final WeComRelayProperties properties;
private final TeacherDirectoryService teacherDirectoryService;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private final Map<String, LocalToken> localTokens = new ConcurrentHashMap<String, LocalToken>();
private volatile CorpAccessToken corpAccessToken;
private volatile PlatformAccessToken platformAccessToken;
public WeComRelayService(WeComRelayProperties properties, TeacherDirectoryService teacherDirectoryService,
RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper) {
this.properties = properties;
this.teacherDirectoryService = teacherDirectoryService;
this.restTemplate = restTemplateBuilder
.setConnectTimeout(java.time.Duration.ofMillis(properties.getConnectTimeoutMs()))
.setReadTimeout(java.time.Duration.ofMillis(properties.getReadTimeoutMs()))
.build();
this.objectMapper = objectMapper;
}
public String buildAuthorizationRedirect(Map<String, String> params) {
String redirectUri = normalizeRedirectUri(firstText(params.get("redirect_uri"), properties.getRedirectUri()));
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(properties.getAuthorizationUri());
addQueryParam(builder, properties.getAuthorizationAppIdParam(), clientId());
addQueryParam(builder, properties.getAuthorizationRedirectUriParam(), redirectUri);
addQueryParam(builder, properties.getAuthorizationResponseTypeParam(), firstText(params.get("response_type"), "code"));
addQueryParam(builder, properties.getAuthorizationScopeParam(), firstText(params.get("scope"), properties.getScope()));
addQueryParam(builder, properties.getAuthorizationQrcodeParam(), properties.getAuthorizationQrcodeValue());
if (StringUtils.hasText(properties.getAgentId())) {
builder.queryParam("agentid", properties.getAgentId());
}
if (StringUtils.hasText(params.get("state"))) {
addQueryParam(builder, properties.getAuthorizationStateParam(), params.get("state"));
}
String target = builder.build().toUriString();
if (properties.isAppendWechatRedirect() && target.indexOf("#") < 0) {
target = target + "#wechat_redirect";
}
log.info("WeCom OAuth relay authorize redirect: redirectUri={}, target={}", redirectUri, target);
return target;
}
public String buildMockAuthorizationRedirect(Map<String, String> params) {
if (!properties.isMockEnabled()) {
return buildAuthorizationRedirect(params);
}
String redirectUri = normalizeRedirectUri(firstText(params.get("redirect_uri"), properties.getRedirectUri()));
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(redirectUri)
.queryParam("code", "WCMOCK-" + UUID.randomUUID().toString().replace("-", ""));
if (StringUtils.hasText(params.get("state"))) {
builder.queryParam("state", params.get("state"));
}
String target = builder.build().toUriString();
log.info("WeCom OAuth relay mock authorize redirect: redirectUri={}, target={}", redirectUri, target);
return target;
}
private void addQueryParam(UriComponentsBuilder builder, String name, String value) {
if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
builder.queryParam(name, value);
}
}
private String normalizeRedirectUri(String redirectUri) {
if (StringUtils.hasText(redirectUri) && redirectUri.contains("{HAP}")
&& StringUtils.hasText(properties.getRedirectUri())) {
return properties.getRedirectUri();
}
validateRedirectUri(redirectUri);
return redirectUri;
}
private void validateRedirectUri(String redirectUri) {
if (!StringUtils.hasText(redirectUri) || !StringUtils.hasText(properties.getAllowedRedirectHost())) {
return;
}
try {
URI uri = new URI(redirectUri);
String host = uri.getHost();
if (!properties.getAllowedRedirectHost().equalsIgnoreCase(host)) {
throw new IllegalArgumentException("redirect_uri host is not allowed: " + host);
}
} catch (URISyntaxException ex) {
throw new IllegalArgumentException("redirect_uri is invalid: " + redirectUri, ex);
}
}
public Map<String, Object> exchangeToken(Map<String, String> params) {
String code = params.get("code");
if (!properties.isEnabled()) {
return oauthError("wecom oauth relay disabled");
}
if (!StringUtils.hasText(code)) {
return oauthError("code is required");
}
if (!StringUtils.hasText(clientId()) || !StringUtils.hasText(clientSecret())) {
return oauthError("wecom app-id and app-secret are required");
}
try {
if (properties.isMockEnabled() && code.startsWith("WCMOCK-")) {
return issueMockToken(code);
}
log.info("Campus OAuth relay real token exchange start: code={}, platformTokenUri={}, userByCodeUri={}",
mask(code), properties.getCodeExchangeUri(), properties.getTokenUserInfoUri());
Map<String, Object> codeUser = getCampusCodeUser(code);
return issueTokenForCodeUser(codeUser);
} catch (RestClientException ex) {
log.error("Campus OAuth relay token exchange request failed: code={}", mask(code), ex);
return oauthError("token exchange request failed: " + ex.getMessage());
} catch (Exception ex) {
log.error("Campus OAuth relay token exchange failed: code={}", mask(code), ex);
return oauthError("token exchange failed: " + ex.getMessage());
}
}
private Map<String, Object> getCampusCodeUser(String code) throws Exception {
if (StringUtils.hasText(properties.getCodeExchangeUri())) {
String accessToken = getPlatformAccessToken();
return getCampusUserByCode(code, accessToken);
}
if (StringUtils.hasText(properties.getAccessTokenUri()) && StringUtils.hasText(properties.getCodeUserInfoUri())) {
log.warn("Campus code exchange uri is empty, falling back to legacy WeCom qyapi flow");
String accessToken = getCorpAccessToken();
return getCodeUser(accessToken, code);
}
throw new IllegalStateException("wecom-relay.code-exchange-uri is required for campus OAuth code exchange");
}
private URI buildPlatformTokenUri() {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(properties.getCodeExchangeUri());
addQueryParam(builder, properties.getCodeExchangeAppIdParam(), clientId());
addQueryParam(builder, properties.getCodeExchangeAppSecretParam(), clientSecret());
return builder.build(true).toUri();
}
private String getPlatformAccessToken() throws Exception {
PlatformAccessToken current = platformAccessToken;
if (current != null && !current.isExpired()) {
return current.getToken();
}
synchronized (this) {
current = platformAccessToken;
if (current != null && !current.isExpired()) {
return current.getToken();
}
URI uri = buildPlatformTokenUri();
log.info("Campus platform token request: uri={}", maskSecret(uri.toString()));
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Map<String, Object> body = parseJson(response.getBody());
log.info("Campus platform token response: httpStatus={}, body={}",
response.getStatusCodeValue(), maskAccessTokenBody(body));
assertCampusSuccess(body, "platform token");
Map<String, Object> data = unwrapData(body);
String token = firstText(stringValue(data.get("access_token")), stringValue(body.get("access_token")));
if (!StringUtils.hasText(token)) {
throw new IllegalStateException("Campus platform token response missing access_token: " + body);
}
int expiresIn = intValue(firstObject(data.get("expires_in"), body.get("expires_in"), null, null), 7200);
int skew = Math.max(properties.getPlatformAccessTokenSkewSeconds(), 0);
Instant expiresAt = Instant.now().plusSeconds(Math.max(expiresIn - skew, 60));
platformAccessToken = new PlatformAccessToken(token, expiresAt);
log.info("Campus platform token cached: expiresAt={}", expiresAt);
return token;
}
}
private Map<String, Object> getCampusUserByCode(String code, String accessToken) throws Exception {
if (!StringUtils.hasText(properties.getTokenUserInfoUri())) {
throw new IllegalStateException("wecom-relay.token-user-info-uri is required");
}
URI uri = UriComponentsBuilder.fromHttpUrl(properties.getTokenUserInfoUri())
.queryParam(properties.getTokenUserInfoTokenParam(), accessToken)
.queryParam(properties.getCodeParam(), code)
.build(true)
.toUri();
log.info("Campus user-by-code request: code={}, token={}, uri={}",
mask(code), mask(accessToken), maskAccessTokenInUri(uri.toString(), accessToken));
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Map<String, Object> body = parseJson(response.getBody());
log.info("Campus user-by-code response: httpStatus={}, body={}", response.getStatusCodeValue(), body);
assertCampusSuccess(body, "user by code");
return unwrapData(body);
}
private Map<String, Object> issueTokenForCodeUser(Map<String, Object> codeUser) {
String workNo = firstText(
roleNumber(codeUser),
stringValue(codeUser.get("number")),
stringValue(codeUser.get("workNo")),
stringValue(codeUser.get("work_no")),
firstText(
stringValue(codeUser.get("workId")),
stringValue(codeUser.get("work_id")),
stringValue(codeUser.get("UserId")),
stringValue(codeUser.get("userid")),
firstText(
stringValue(codeUser.get("user_id")),
stringValue(codeUser.get("uid")),
stringValue(codeUser.get("id")))));
String mobile = firstText(
stringValue(codeUser.get("mobile")),
stringValue(codeUser.get("mobilePhone")),
stringValue(codeUser.get("phone")));
TeacherInfo teacher = teacherDirectoryService.findByWorkNo(workNo);
if (teacher == null && StringUtils.hasText(mobile)) {
teacher = teacherDirectoryService.findByMobile(mobile);
}
if (teacher == null) {
String missingKey = StringUtils.hasText(workNo) ? workNo : firstText(mobile, "unknown");
log.warn("Campus OAuth relay teacher cache miss: workNo={}, platformUid={}, mobile={}, identity={}, teacherCount={}",
workNo, codeUser.get("uid"), mobile, roleIdentity(codeUser), teacherDirectoryService.size());
return oauthError("teacher not found: " + missingKey);
}
String localToken = "WCT-" + UUID.randomUUID().toString().replace("-", "");
Instant expiresAt = Instant.now().plusSeconds(Math.max(properties.getTokenExpiresIn(), 60));
localTokens.put(localToken, new LocalToken(localToken, expiresAt, teacher,
codeUser, new LinkedHashMap<String, Object>()));
cleanupExpiredTokens();
Map<String, Object> body = new LinkedHashMap<String, Object>();
body.put("access_token", localToken);
body.put("token_type", "Bearer");
body.put("expires_in", properties.getTokenExpiresIn());
log.info("Campus OAuth relay local token issued: token={}, workNo={}, expiresAt={}",
mask(localToken), teacher.getWorkNo(), expiresAt);
return body;
}
private Map<String, Object> issueMockToken(String code) {
TeacherInfo teacher = new TeacherInfo(properties.getMockWorkNo(), properties.getMockName(),
properties.getMockEmail(), properties.getMockMobile());
Map<String, Object> codeUser = new LinkedHashMap<String, Object>();
codeUser.put("mock", true);
codeUser.put("code", code);
codeUser.put("UserId", teacher.getWorkNo());
codeUser.put("mobile", teacher.getMobile());
String localToken = "WCT-" + UUID.randomUUID().toString().replace("-", "");
Instant expiresAt = Instant.now().plusSeconds(Math.max(properties.getTokenExpiresIn(), 60));
localTokens.put(localToken, new LocalToken(localToken, expiresAt, teacher,
codeUser, new LinkedHashMap<String, Object>()));
Map<String, Object> body = new LinkedHashMap<String, Object>();
body.put("access_token", localToken);
body.put("token_type", "Bearer");
body.put("expires_in", properties.getTokenExpiresIn());
log.info("WeCom OAuth relay mock local token issued: token={}, workNo={}, expiresAt={}",
mask(localToken), teacher.getWorkNo(), expiresAt);
return body;
}
public Map<String, Object> userInfo(String token) {
if (!properties.isEnabled()) {
return resultError("wecom oauth relay disabled");
}
if (!StringUtils.hasText(token)) {
return resultError("token or access_token is required");
}
LocalToken localToken = localTokens.get(token);
if (localToken == null || localToken.isExpired()) {
if (localToken != null) {
localTokens.remove(token);
}
return resultError("token expired or invalid");
}
TeacherInfo teacher = localToken.getTeacher();
String email = teacher.getEmail();
if (!StringUtils.hasText(email) && properties.isSynthesizeEmail()
&& StringUtils.hasText(teacher.getWorkNo()) && StringUtils.hasText(properties.getEmailDomain())) {
email = teacher.getWorkNo() + "@" + properties.getEmailDomain();
}
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("uid", teacher.getWorkNo());
data.put("name", firstText(teacher.getName(), stringValue(localToken.getCodeUser().get("realname")), teacher.getWorkNo()));
data.put("email", firstText(email, stringValue(localToken.getCodeUser().get("email"))));
data.put("mobilePhone", firstText(teacher.getMobile(), stringValue(localToken.getCodeUser().get("mobile"))));
data.put("mobile", firstText(teacher.getMobile(), stringValue(localToken.getCodeUser().get("mobile"))));
data.put("workId", teacher.getWorkNo());
data.put("raw", localToken.getCodeUser());
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("data", data);
log.info("WeCom OAuth relay Mingdao userInfo result: workNo={}, token={}",
teacher.getWorkNo(), mask(token));
return result;
}
public String maskForLog(String value) {
return mask(value);
}
private String getCorpAccessToken() throws Exception {
CorpAccessToken current = corpAccessToken;
if (current != null && !current.isExpired()) {
return current.getToken();
}
synchronized (this) {
current = corpAccessToken;
if (current != null && !current.isExpired()) {
return current.getToken();
}
URI uri = UriComponentsBuilder.fromHttpUrl(properties.getAccessTokenUri())
.queryParam("corpid", clientId())
.queryParam("corpsecret", clientSecret())
.build(true)
.toUri();
log.info("WeCom corp access token request: uri={}", maskSecret(uri.toString()));
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Map<String, Object> body = parseJson(response.getBody());
assertWeComSuccess(body, "corp access token");
String token = stringValue(body.get("access_token"));
if (!StringUtils.hasText(token)) {
throw new IllegalStateException("WeCom access token response missing access_token");
}
int expiresIn = intValue(body.get("expires_in"), 7200);
int skew = Math.max(properties.getCorpAccessTokenSkewSeconds(), 0);
Instant expiresAt = Instant.now().plusSeconds(Math.max(expiresIn - skew, 60));
corpAccessToken = new CorpAccessToken(token, expiresAt);
log.info("WeCom corp access token refreshed: expiresAt={}", expiresAt);
return token;
}
}
private Map<String, Object> getCodeUser(String accessToken, String code) throws Exception {
URI uri = UriComponentsBuilder.fromHttpUrl(properties.getCodeUserInfoUri())
.queryParam("access_token", accessToken)
.queryParam("code", code)
.build(true)
.toUri();
log.info("WeCom code userInfo request: code={}, uri={}", mask(code), maskAccessTokenInUri(uri.toString(), accessToken));
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Map<String, Object> body = parseJson(response.getBody());
assertWeComSuccess(body, "code user info");
log.info("WeCom code userInfo response: code={}, body={}", mask(code), body);
return body;
}
private Map<String, Object> getUserDetailQuietly(String accessToken, String userId) {
if (!StringUtils.hasText(properties.getUserDetailUri())) {
return new LinkedHashMap<String, Object>();
}
try {
URI uri = UriComponentsBuilder.fromHttpUrl(properties.getUserDetailUri())
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build(true)
.toUri();
log.info("WeCom user detail request: userId={}, uri={}", userId, maskAccessTokenInUri(uri.toString(), accessToken));
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Map<String, Object> body = parseJson(response.getBody());
assertWeComSuccess(body, "user detail");
return body;
} catch (Exception ex) {
log.warn("WeCom user detail request ignored: userId={}, message={}", userId, ex.getMessage());
return new LinkedHashMap<String, Object>();
}
}
private void assertWeComSuccess(Map<String, Object> body, String action) {
Object errCode = body.get("errcode");
if (errCode == null || "0".equals(String.valueOf(errCode))) {
return;
}
throw new IllegalStateException("WeCom " + action + " failed: " + body);
}
private void assertCampusSuccess(Map<String, Object> body, String action) {
Object success = body.get("success");
Object code = firstObject(body.get("e"), body.get("code"), body.get("errcode"), body.get("errorCode"));
if (code == null) {
code = body.get("status");
}
if (Boolean.FALSE.equals(success)) {
throw new IllegalStateException("Campus " + action + " failed: " + body);
}
if (code == null || "0".equals(String.valueOf(code)) || "200".equals(String.valueOf(code))) {
return;
}
throw new IllegalStateException("Campus " + action + " failed: " + body);
}
private Map<String, Object> unwrapData(Map<String, Object> body) {
Object data = firstObject(body.get("d"), body.get("data"), body.get("result"), body.get("user"));
if (data == null) {
data = body.get("userInfo");
}
if (data instanceof Map) {
return (Map<String, Object>) data;
}
return body;
}
private Map<String, Object> maskAccessTokenBody(Map<String, Object> body) {
Map<String, Object> masked = new LinkedHashMap<String, Object>(body);
maskAccessTokenField(masked);
Object data = masked.get("d");
if (data instanceof Map) {
Map<String, Object> maskedData = new LinkedHashMap<String, Object>((Map<String, Object>) data);
maskAccessTokenField(maskedData);
masked.put("d", maskedData);
}
return masked;
}
private void maskAccessTokenField(Map<String, Object> body) {
Object accessToken = body.get("access_token");
if (accessToken != null) {
body.put("access_token", mask(String.valueOf(accessToken)));
}
}
private void cleanupExpiredTokens() {
for (Map.Entry<String, LocalToken> entry : localTokens.entrySet()) {
if (entry.getValue().isExpired()) {
localTokens.remove(entry.getKey());
}
}
}
private Map<String, Object> oauthError(String message) {
Map<String, Object> body = new LinkedHashMap<String, Object>();
body.put("error", "invalid_request");
body.put("error_description", message);
return body;
}
private Map<String, Object> resultError(String message) {
Map<String, Object> body = new LinkedHashMap<String, Object>();
body.put("success", false);
body.put("error_code", 10101);
body.put("error_msg", message);
return body;
}
private Map<String, Object> parseJson(String json) throws Exception {
if (!StringUtils.hasText(json)) {
return new LinkedHashMap<String, Object>();
}
return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {
});
}
private String firstText(String first, String second) {
return StringUtils.hasText(first) ? first : second;
}
private Object firstObject(Object first, Object second, Object third, Object fourth) {
if (first != null) {
return first;
}
if (second != null) {
return second;
}
return third != null ? third : fourth;
}
private String roleNumber(Map<String, Object> codeUser) {
Object role = codeUser.get("role");
if (role instanceof Map) {
return stringValue(((Map<?, ?>) role).get("number"));
}
return null;
}
private String roleIdentity(Map<String, Object> codeUser) {
Object role = codeUser.get("role");
if (role instanceof Map) {
return stringValue(((Map<?, ?>) role).get("identity"));
}
return null;
}
private String firstText(String first, String second, String third) {
return StringUtils.hasText(first) ? first : (StringUtils.hasText(second) ? second : third);
}
private String firstText(String first, String second, String third, String fourth) {
return StringUtils.hasText(first) ? first : firstText(second, third, fourth);
}
private String firstText(String first, String second, String third, String fourth, String fifth) {
return StringUtils.hasText(first) ? first : firstText(second, third, fourth, fifth);
}
private String stringValue(Object value) {
return value == null ? null : String.valueOf(value);
}
private int intValue(Object value, int defaultValue) {
if (value instanceof Number) {
return ((Number) value).intValue();
}
if (value != null) {
try {
return Integer.parseInt(String.valueOf(value));
} catch (Exception ignored) {
}
}
return defaultValue;
}
private String mask(String value) {
if (!StringUtils.hasText(value)) {
return "";
}
if (value.length() <= 12) {
return value.substring(0, Math.min(value.length(), 4)) + "***";
}
return value.substring(0, 6) + "***" + value.substring(value.length() - 6);
}
private String maskSecret(String value) {
if (!StringUtils.hasText(value)) {
return value;
}
return value.replace(clientSecret(), "***");
}
private String clientId() {
return firstText(properties.getAppId(), properties.getCorpId());
}
private String clientSecret() {
return firstText(properties.getAppSecret(), properties.getCorpSecret());
}
private String maskAccessTokenInUri(String uri, String token) {
if (!StringUtils.hasText(uri) || !StringUtils.hasText(token)) {
return uri;
}
return uri.replace(token, mask(token));
}
private static class CorpAccessToken {
private final String token;
private final Instant expiresAt;
private CorpAccessToken(String token, Instant expiresAt) {
this.token = token;
this.expiresAt = expiresAt;
}
private String getToken() {
return token;
}
private boolean isExpired() {
return Instant.now().isAfter(expiresAt);
}
}
private static class PlatformAccessToken {
private final String token;
private final Instant expiresAt;
private PlatformAccessToken(String token, Instant expiresAt) {
this.token = token;
this.expiresAt = expiresAt;
}
private String getToken() {
return token;
}
private boolean isExpired() {
return Instant.now().isAfter(expiresAt);
}
}
private static class LocalToken {
private final String token;
private final Instant expiresAt;
private final TeacherInfo teacher;
private final Map<String, Object> codeUser;
private final Map<String, Object> userDetail;
private LocalToken(String token, Instant expiresAt, TeacherInfo teacher,
Map<String, Object> codeUser, Map<String, Object> userDetail) {
this.token = token;
this.expiresAt = expiresAt;
this.teacher = teacher;
this.codeUser = codeUser;
this.userDetail = userDetail;
}
private TeacherInfo getTeacher() {
return teacher;
}
private Map<String, Object> getCodeUser() {
return codeUser;
}
private boolean isExpired() {
return Instant.now().isAfter(expiresAt);
}
}
}
package com.jd.easywalk.casrelay;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class WeComRelayStartupLogger {
private static final Logger log = LoggerFactory.getLogger(WeComRelayStartupLogger.class);
private final WeComRelayProperties properties;
public WeComRelayStartupLogger(WeComRelayProperties properties) {
this.properties = properties;
}
@PostConstruct
public void logConfig() {
log.info("WeCom relay config: enabled={}, appId={}, platformDomain={}, authorizationUri={}, "
+ "redirectUri={}, appIdParam={}, redirectParam={}, qrcodeParam={}, codeExchangeUri={}, mockEnabled={}",
properties.isEnabled(), properties.getAppId(), properties.getPlatformDomain(),
properties.getAuthorizationUri(), properties.getRedirectUri(),
properties.getAuthorizationAppIdParam(), properties.getAuthorizationRedirectUriParam(),
properties.getAuthorizationQrcodeParam(), properties.getCodeExchangeUri(), properties.isMockEnabled());
}
}
org.springframework.context.ApplicationContextInitializer=\
com.jd.easywalk.casrelay.DotenvPropertySourceInitializer
......@@ -42,3 +42,45 @@ oauth-relay:
expires-in: ${OAUTH_EXPIRES_IN:7200}
email-domain: ${OAUTH_EMAIL_DOMAIN:xidian.edu.cn}
synthesize-email: ${OAUTH_SYNTHESIZE_EMAIL:true}
wecom-relay:
enabled: ${WECOM_RELAY_ENABLED:false}
app-id: ${WECOM_APP_ID:200260525100013951}
app-secret: ${WECOM_APP_SECRET:}
platform-domain: ${WECOM_PLATFORM_DOMAIN:xxcapp.xidian.edu.cn}
corp-id: ${WECOM_CORP_ID:}
agent-id: ${WECOM_AGENT_ID:}
corp-secret: ${WECOM_CORP_SECRET:}
authorization-uri: ${WECOM_AUTHORIZATION_URI:https://xxcapp.xidian.edu.cn/uc/api/oauth/index}
authorization-app-id-param: ${WECOM_AUTHORIZATION_APP_ID_PARAM:appid}
authorization-redirect-uri-param: ${WECOM_AUTHORIZATION_REDIRECT_URI_PARAM:redirect}
authorization-response-type-param: ${WECOM_AUTHORIZATION_RESPONSE_TYPE_PARAM:}
authorization-scope-param: ${WECOM_AUTHORIZATION_SCOPE_PARAM:}
authorization-state-param: ${WECOM_AUTHORIZATION_STATE_PARAM:state}
authorization-qrcode-param: ${WECOM_AUTHORIZATION_QRCODE_PARAM:qrcode}
authorization-qrcode-value: ${WECOM_AUTHORIZATION_QRCODE_VALUE:1}
code-exchange-uri: ${WECOM_CODE_EXCHANGE_URI:https://xxcapp.xidian.edu.cn/api/third/get-token}
token-user-info-uri: ${WECOM_TOKEN_USER_INFO_URI:https://xxcapp.xidian.edu.cn/uc/api/oauth/user-by-code}
code-param: ${WECOM_CODE_PARAM:code}
code-exchange-app-id-param: ${WECOM_CODE_EXCHANGE_APP_ID_PARAM:appid}
code-exchange-app-secret-param: ${WECOM_CODE_EXCHANGE_APP_SECRET_PARAM:appsecret}
token-user-info-token-param: ${WECOM_TOKEN_USER_INFO_TOKEN_PARAM:access_token}
platform-access-token-skew-seconds: ${WECOM_PLATFORM_ACCESS_TOKEN_SKEW_SECONDS:300}
access-token-uri: ${WECOM_ACCESS_TOKEN_URI:}
code-user-info-uri: ${WECOM_CODE_USER_INFO_URI:}
user-detail-uri: ${WECOM_USER_DETAIL_URI:}
redirect-uri: ${WECOM_REDIRECT_URI:}
allowed-redirect-host: ${WECOM_ALLOWED_REDIRECT_HOST:nercwbs-lms.xidian.edu.cn}
scope: ${WECOM_SCOPE:snsapi_base}
token-expires-in: ${WECOM_TOKEN_EXPIRES_IN:7200}
corp-access-token-skew-seconds: ${WECOM_CORP_ACCESS_TOKEN_SKEW_SECONDS:300}
email-domain: ${WECOM_EMAIL_DOMAIN:xidian.edu.cn}
synthesize-email: ${WECOM_SYNTHESIZE_EMAIL:true}
append-wechat-redirect: ${WECOM_APPEND_WECHAT_REDIRECT:true}
connect-timeout-ms: ${WECOM_CONNECT_TIMEOUT_MS:5000}
read-timeout-ms: ${WECOM_READ_TIMEOUT_MS:10000}
mock-enabled: ${WECOM_MOCK_ENABLED:false}
mock-work-no: ${WECOM_MOCK_WORK_NO:20240001}
mock-name: ${WECOM_MOCK_NAME:MockUser}
mock-email: ${WECOM_MOCK_EMAIL:20240001@xidian.edu.cn}
mock-mobile: ${WECOM_MOCK_MOBILE:13800000000}
{
"mode": "common-oauth2",
"name": "xidian-wecom-oauth-relay",
"oauth2": {
"oauth2Url": "https://casrelay.xidian.edu.cn",
"clientId": "mingdao-wecom-relay",
"clientSecret": "mingdao-wecom-relay-secret",
"idParamName": "client_id",
"secretParamName": "client_secret",
"accessTokenParamName": "access_token",
"redirectUri": "https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2",
"authorizePath": "/wecom/oauth2/authorize",
"tokenPath": "/wecom/oauth2/access_token",
"userInfoUrl": "https://casrelay.xidian.edu.cn/wecom/oauth2/userinfo",
"tokenKeyParamName": "token",
"authorizationMethod": "body",
"bodyFormat": "form",
"scope": "snsapi_base",
"params": {
"UserId": "uid",
"Name": "name",
"Email": "email",
"Mobile": "mobilePhone"
},
"autoRegister": true,
"projectId": "22070640-479c-493b-9045-9c39e303cf2a"
}
}
{
"mode": "common-oauth2",
"name": "xidian-wecom-oauth-relay-mock",
"oauth2": {
"oauth2Url": "https://casrelay.xidian.edu.cn",
"clientId": "mingdao-wecom-relay",
"clientSecret": "mingdao-wecom-relay-secret",
"idParamName": "client_id",
"secretParamName": "client_secret",
"accessTokenParamName": "access_token",
"redirectUri": "https://nercwbs-lms.xidian.edu.cn/orgsso/oauth2",
"authorizePath": "/wecom/oauth2/mock/authorize",
"tokenPath": "/wecom/oauth2/access_token",
"userInfoUrl": "https://casrelay.xidian.edu.cn/wecom/oauth2/userinfo",
"tokenKeyParamName": "token",
"authorizationMethod": "body",
"bodyFormat": "form",
"scope": "",
"params": {
"UserId": "uid",
"Name": "name",
"Email": "email",
"Mobile": "mobilePhone"
},
"autoRegister": true,
"projectId": "22070640-479c-493b-9045-9c39e303cf2a"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment