提交 fe82cb8c 编写于 作者: M MaxKey

Merge branch 'main' of https://gitee.com/dromara/MaxKey

...@@ -395,6 +395,13 @@ subprojects { ...@@ -395,6 +395,13 @@ subprojects {
implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}" implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}"
//阿里云 //阿里云
implementation group: 'com.aliyun', name: 'aliyun-java-sdk-core', version: "${aliyunjavasdkcoreVersion}" implementation group: 'com.aliyun', name: 'aliyun-java-sdk-core', version: "${aliyunjavasdkcoreVersion}"
// https://mvnrepository.com/artifact/io.opentracing/opentracing-util
implementation 'io.opentracing:opentracing-util:0.33.0'
// https://mvnrepository.com/artifact/io.opentracing/opentracing-api
implementation 'io.opentracing:opentracing-api:0.33.0'
implementation 'io.opentracing:opentracing-noop:0.33.0'
//腾讯云 //腾讯云
implementation group: 'com.tencentcloudapi', name: 'tencentcloud-sdk-java', version: "${tencentcloudsdkjavaVersion}" implementation group: 'com.tencentcloudapi', name: 'tencentcloud-sdk-java', version: "${tencentcloudsdkjavaVersion}"
//json //json
......
...@@ -71,7 +71,7 @@ public class SmsOtpAuthnService { ...@@ -71,7 +71,7 @@ public class SmsOtpAuthnService {
if(smsProvider.getProvider().equalsIgnoreCase("aliyun")) { if(smsProvider.getProvider().equalsIgnoreCase("aliyun")) {
SmsOtpAuthnAliyun aliyun = new SmsOtpAuthnAliyun( SmsOtpAuthnAliyun aliyun = new SmsOtpAuthnAliyun(
smsProvider.getAppKey(), smsProvider.getAppKey(),
smsProvider.getAppSecret(), PasswordReciprocal.getInstance().decoder(smsProvider.getAppSecret()),
smsProvider.getTemplateId(), smsProvider.getTemplateId(),
smsProvider.getSignName() smsProvider.getSignName()
); );
...@@ -82,7 +82,7 @@ public class SmsOtpAuthnService { ...@@ -82,7 +82,7 @@ public class SmsOtpAuthnService {
}else if(smsProvider.getProvider().equalsIgnoreCase("tencentcloud")) { }else if(smsProvider.getProvider().equalsIgnoreCase("tencentcloud")) {
SmsOtpAuthnTencentCloud tencentCloud = new SmsOtpAuthnTencentCloud( SmsOtpAuthnTencentCloud tencentCloud = new SmsOtpAuthnTencentCloud(
smsProvider.getAppKey(), smsProvider.getAppKey(),
smsProvider.getAppSecret(), PasswordReciprocal.getInstance().decoder(smsProvider.getAppSecret()),
smsProvider.getSmsSdkAppId(), smsProvider.getSmsSdkAppId(),
smsProvider.getTemplateId(), smsProvider.getTemplateId(),
smsProvider.getSignName() smsProvider.getSignName()
...@@ -94,7 +94,7 @@ public class SmsOtpAuthnService { ...@@ -94,7 +94,7 @@ public class SmsOtpAuthnService {
}else if(smsProvider.getProvider().equalsIgnoreCase("neteasesms")) { }else if(smsProvider.getProvider().equalsIgnoreCase("neteasesms")) {
SmsOtpAuthnYunxin yunxin = new SmsOtpAuthnYunxin( SmsOtpAuthnYunxin yunxin = new SmsOtpAuthnYunxin(
smsProvider.getAppKey(), smsProvider.getAppKey(),
smsProvider.getAppSecret(), PasswordReciprocal.getInstance().decoder(smsProvider.getAppSecret()),
smsProvider.getTemplateId() smsProvider.getTemplateId()
); );
if(redisOptTokenStore != null) { if(redisOptTokenStore != null) {
......
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthHuaweiScope;
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;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
public class AuthMaxkeyRequest extends AuthDefaultRequest {
public static final String KEY = "maxkey";
public AuthMaxkeyRequest(AuthConfig config) {
super(config, WeLinkAuthDefaultSource.HUAWEI_WELINK);
}
public AuthMaxkeyRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, MaxkeyAuthDefaultSource.MAXKEY, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return null;
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
return null;
}
}
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhyd.oauth.request;
import me.zhyd.oauth.config.AuthSource;
public enum MaxkeyAuthDefaultSource implements AuthSource{
MAXKEY {
@Override
public String authorize() {
return "https://login.welink.huaweicloud.com/connect/oauth2/sns_authorize";
}
@Override
public String accessToken() {
return "https://open.welink.huaweicloud.com/api/auth/v2/tickets";
}
@Override
public String userInfo() {
return "https://open.welink.huaweicloud.com/api/contact/v1/users";
}
@Override
public String refresh() {
return "";
}
@Override
public Class<? extends AuthDefaultRequest> getTargetClass() {
return AuthHuaweiWeLinkRequest.class;
}
}
}
...@@ -22,6 +22,8 @@ package org.maxkey.authn.support.socialsignon; ...@@ -22,6 +22,8 @@ package org.maxkey.authn.support.socialsignon;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import me.zhyd.oauth.request.AuthMaxkeyRequest;
import org.apache.commons.lang3.StringUtils;
import org.maxkey.authn.LoginCredential; import org.maxkey.authn.LoginCredential;
import org.maxkey.authn.annotation.CurrentUser; import org.maxkey.authn.annotation.CurrentUser;
import org.maxkey.authn.jwt.AuthJwt; import org.maxkey.authn.jwt.AuthJwt;
...@@ -30,18 +32,18 @@ import org.maxkey.entity.Message; ...@@ -30,18 +32,18 @@ import org.maxkey.entity.Message;
import org.maxkey.entity.SocialsAssociate; import org.maxkey.entity.SocialsAssociate;
import org.maxkey.entity.SocialsProvider; import org.maxkey.entity.SocialsProvider;
import org.maxkey.entity.UserInfo; import org.maxkey.entity.UserInfo;
import org.maxkey.uuid.UUID;
import org.maxkey.web.WebContext; import org.maxkey.web.WebContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.request.AuthRequest;
import java.util.Map;
/** /**
* @author Crystal.Sea * @author Crystal.Sea
* *
...@@ -50,7 +52,7 @@ import me.zhyd.oauth.request.AuthRequest; ...@@ -50,7 +52,7 @@ import me.zhyd.oauth.request.AuthRequest;
@RequestMapping(value = "/logon/oauth20") @RequestMapping(value = "/logon/oauth20")
public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
final static Logger _logger = LoggerFactory.getLogger(SocialSignOnEndpoint.class); final static Logger _logger = LoggerFactory.getLogger(SocialSignOnEndpoint.class);
@RequestMapping(value={"/authorize/{provider}"}, method = RequestMethod.GET) @RequestMapping(value={"/authorize/{provider}"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody
public ResponseEntity<?> authorize( HttpServletRequest request, public ResponseEntity<?> authorize( HttpServletRequest request,
...@@ -59,13 +61,13 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -59,13 +61,13 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
_logger.trace("SocialSignOn provider : " + provider); _logger.trace("SocialSignOn provider : " + provider);
String instId = WebContext.getInst().getId(); String instId = WebContext.getInst().getId();
String originURL =WebContext.getHttpContextPath(request,false); String originURL =WebContext.getHttpContextPath(request,false);
String authorizationUrl = String authorizationUrl =
buildAuthRequest( buildAuthRequest(
instId, instId,
provider, provider,
originURL + applicationConfig.getFrontendUri() originURL + applicationConfig.getFrontendUri()
).authorize(authTokenService.genRandomJwt()); ).authorize(authTokenService.genRandomJwt());
_logger.trace("authorize SocialSignOn : " + authorizationUrl); _logger.trace("authorize SocialSignOn : " + authorizationUrl);
return new Message<Object>((Object)authorizationUrl).buildResponse(); return new Message<Object>((Object)authorizationUrl).buildResponse();
} }
...@@ -85,7 +87,8 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -85,7 +87,8 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
if(authRequest == null ) { if(authRequest == null ) {
_logger.error("build authRequest fail ."); _logger.error("build authRequest fail .");
} }
String state = authTokenService.genRandomJwt(); String state = UUID.generate().toString();
//String state = authTokenService.genRandomJwt();
authRequest.authorize(state); authRequest.authorize(state);
SocialsProvider socialSignOnProvider = socialSignOnProviderService.get(instId,provider); SocialsProvider socialSignOnProvider = socialSignOnProviderService.get(instId,provider);
...@@ -94,10 +97,14 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -94,10 +97,14 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
scanQrProvider.setRedirectUri( scanQrProvider.setRedirectUri(
socialSignOnProviderService.getRedirectUri( socialSignOnProviderService.getRedirectUri(
originURL + applicationConfig.getFrontendUri(), provider)); originURL + applicationConfig.getFrontendUri(), provider));
//缓存state票据在缓存或者是redis中五分钟过期
if (provider.equalsIgnoreCase(AuthMaxkeyRequest.KEY)) {
socialSignOnProviderService.setToken(state);
}
return new Message<SocialsProvider>(scanQrProvider).buildResponse(); return new Message<SocialsProvider>(scanQrProvider).buildResponse();
} }
@RequestMapping(value={"/bind/{provider}"}, method = RequestMethod.GET) @RequestMapping(value={"/bind/{provider}"}, method = RequestMethod.GET)
public ResponseEntity<?> bind(@PathVariable String provider, public ResponseEntity<?> bind(@PathVariable String provider,
...@@ -105,7 +112,7 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -105,7 +112,7 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
HttpServletRequest request) { HttpServletRequest request) {
//auth call back may exception //auth call back may exception
try { try {
String originURL =WebContext.getHttpContextPath(request,false); String originURL = WebContext.getHttpContextPath(request,false);
SocialsAssociate socialsAssociate = SocialsAssociate socialsAssociate =
this.authCallback(userInfo.getInstId(),provider,originURL + applicationConfig.getFrontendUri()); this.authCallback(userInfo.getInstId(),provider,originURL + applicationConfig.getFrontendUri());
socialsAssociate.setSocialUserInfo(accountJsonString); socialsAssociate.setSocialUserInfo(accountJsonString);
...@@ -125,6 +132,8 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -125,6 +132,8 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
return new Message<AuthJwt>(Message.ERROR).buildResponse(); return new Message<AuthJwt>(Message.ERROR).buildResponse();
} }
@RequestMapping(value={"/callback/{provider}"}, method = RequestMethod.GET) @RequestMapping(value={"/callback/{provider}"}, method = RequestMethod.GET)
public ResponseEntity<?> callback(@PathVariable String provider, public ResponseEntity<?> callback(@PathVariable String provider,
HttpServletRequest request) { HttpServletRequest request) {
...@@ -134,15 +143,20 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -134,15 +143,20 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
String instId = WebContext.getInst().getId(); String instId = WebContext.getInst().getId();
SocialsAssociate socialsAssociate = SocialsAssociate socialsAssociate =
this.authCallback(instId,provider,originURL + applicationConfig.getFrontendUri()); this.authCallback(instId,provider,originURL + applicationConfig.getFrontendUri());
SocialsAssociate socialssssociate1 = this.socialsAssociateService.get(socialsAssociate);
socialsAssociate=this.socialsAssociateService.get(socialsAssociate); _logger.debug("Loaded SocialSignOn Socials Associate : "+socialssssociate1);
_logger.debug("Loaded SocialSignOn Socials Associate : "+socialsAssociate);
if(null == socialsAssociate) {
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
if (null == socialssssociate1) {
//如果存在第三方ID并且在数据库无法找到映射关系,则进行绑定逻辑
if (StringUtils.isNotEmpty(socialsAssociate.getSocialUserId())) {
//返回message为第三方用户标识
return new Message<AuthJwt>(Message.PROMPT,socialsAssociate.getSocialUserId()).buildResponse();
}
}
socialsAssociate = socialssssociate1;
_logger.debug("Social Sign On from {} mapping to user {}", _logger.debug("Social Sign On from {} mapping to user {}",
socialsAssociate.getProvider(),socialsAssociate.getUsername()); socialsAssociate.getProvider(),socialsAssociate.getUsername());
...@@ -163,4 +177,99 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{ ...@@ -163,4 +177,99 @@ public class SocialSignOnEndpoint extends AbstractSocialSignOnEndpoint{
return new Message<AuthJwt>(Message.ERROR).buildResponse(); return new Message<AuthJwt>(Message.ERROR).buildResponse();
} }
} }
/**
* 提供给第三方应用关联用户接口
* @return
*/
@RequestMapping(value={"/workweixin/qr/auth/login"}, method = {RequestMethod.POST})
public ResponseEntity<?> qrAuthLogin(
@RequestParam Map<String, String> param,
HttpServletRequest request) {
try {
if (null == param){
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
String token = param.get("token");
String username = param.get("username");
//判断token是否合法
String redisusername = this.socialSignOnProviderService.getToken(token);
if (StringUtils.isNotEmpty(redisusername)){
//设置token和用户绑定
boolean flag = this.socialSignOnProviderService.bindtoken(token,username);
if (flag) {
return new Message<AuthJwt>().buildResponse();
}
} else {
return new Message<AuthJwt>(Message.WARNING,"Invalid token").buildResponse();
}
}catch(Exception e) {
_logger.error("qrAuthLogin Exception ",e);
}
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
/**
* maxkey 监听扫码回调
* @param provider
* @param state
* @param request
* @return
*/
@RequestMapping(value={"/qrcallback/{provider}/{state}"}, method = RequestMethod.GET)
public ResponseEntity<?> qrcallback(@PathVariable String provider,@PathVariable String state,
HttpServletRequest request) {
try {
//判断只有maxkey扫码
if (!provider.equalsIgnoreCase(AuthMaxkeyRequest.KEY)) {
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
String loginName = socialSignOnProviderService.getToken(state);
if (StringUtils.isEmpty(loginName)) {
//二维码过期
return new Message<AuthJwt>(Message.PROMPT).buildResponse();
}
if("-1".equalsIgnoreCase(loginName)){
//暂无用户扫码
return new Message<AuthJwt>(Message.WARNING).buildResponse();
}
String instId = WebContext.getInst().getId();
SocialsAssociate socialsAssociate = new SocialsAssociate();
socialsAssociate.setProvider(provider);
socialsAssociate.setSocialUserId(loginName);
socialsAssociate.setInstId(instId);
socialsAssociate = this.socialsAssociateService.get(socialsAssociate);
_logger.debug("qrcallback Loaded SocialSignOn Socials Associate : "+socialsAssociate);
if(null == socialsAssociate) {
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
_logger.debug("qrcallback Social Sign On from {} mapping to user {}", socialsAssociate.getProvider(),socialsAssociate.getUsername());
LoginCredential loginCredential =new LoginCredential(
socialsAssociate.getUsername(),"",ConstsLoginType.SOCIALSIGNON);
SocialsProvider socialSignOnProvider = socialSignOnProviderService.get(instId,provider);
loginCredential.setProvider(socialSignOnProvider.getProviderName());
Authentication authentication = authenticationProvider.authenticate(loginCredential,true);
//socialsAssociate.setAccessToken(JsonUtils.object2Json(this.accessToken));
socialsAssociate.setSocialUserInfo(accountJsonString);
//socialsAssociate.setExAttribute(JsonUtils.object2Json(accessToken.getResponseObject()));
this.socialsAssociateService.update(socialsAssociate);
return new Message<AuthJwt>(authTokenService.genAuthJwt(authentication)).buildResponse();
}catch(Exception e) {
_logger.error("qrcallback Exception ",e);
return new Message<AuthJwt>(Message.ERROR).buildResponse();
}
}
} }
...@@ -24,6 +24,7 @@ import java.util.HashMap; ...@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.maxkey.authn.support.socialsignon.token.RedisTokenStore;
import org.maxkey.constants.ConstsTimeInterval; import org.maxkey.constants.ConstsTimeInterval;
import org.maxkey.crypto.password.PasswordReciprocal; import org.maxkey.crypto.password.PasswordReciprocal;
import org.maxkey.entity.SocialsProvider; import org.maxkey.entity.SocialsProvider;
...@@ -54,6 +55,9 @@ public class SocialSignOnProviderService{ ...@@ -54,6 +55,9 @@ public class SocialSignOnProviderService{
HashMap<String ,SocialsProvider>socialSignOnProviderMaps = new HashMap<String ,SocialsProvider>(); HashMap<String ,SocialsProvider>socialSignOnProviderMaps = new HashMap<String ,SocialsProvider>();
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
RedisTokenStore redisTokenStore;
public SocialSignOnProviderService(JdbcTemplate jdbcTemplate) { public SocialSignOnProviderService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate=jdbcTemplate; this.jdbcTemplate=jdbcTemplate;
...@@ -62,6 +66,17 @@ public class SocialSignOnProviderService{ ...@@ -62,6 +66,17 @@ public class SocialSignOnProviderService{
public SocialsProvider get(String instId,String provider){ public SocialsProvider get(String instId,String provider){
return socialSignOnProviderMaps.get(instId + "_" + provider); return socialSignOnProviderMaps.get(instId + "_" + provider);
} }
public void setToken(String token){
this.redisTokenStore.store(token);
}
public boolean bindtoken(String token,String loginName){
return this.redisTokenStore.bindtoken(token,loginName);
}
public String getToken(String token){
return this.redisTokenStore.get(token);
}
public String getRedirectUri(String baseUri,String provider) { public String getRedirectUri(String baseUri,String provider) {
return baseUri + "/passport/callback/"+provider; return baseUri + "/passport/callback/"+provider;
...@@ -129,10 +144,10 @@ public class SocialSignOnProviderService{ ...@@ -129,10 +144,10 @@ public class SocialSignOnProviderService{
authRequest = new AuthWeChatEnterpriseWebRequest(authConfig); authRequest = new AuthWeChatEnterpriseWebRequest(authConfig);
}else if(provider.equalsIgnoreCase("welink")) { }else if(provider.equalsIgnoreCase("welink")) {
authRequest = new AuthHuaweiWeLinkRequest(authConfig); authRequest = new AuthHuaweiWeLinkRequest(authConfig);
} }else if(provider.equalsIgnoreCase("maxkey")) {
authRequest = new AuthMaxkeyRequest(authConfig);
}
return authRequest; return authRequest;
} }
...@@ -234,4 +249,9 @@ public class SocialSignOnProviderService{ ...@@ -234,4 +249,9 @@ public class SocialSignOnProviderService{
return socialsProvider; return socialsProvider;
} }
} }
public void setRedisTokenStore(RedisTokenStore redisTokenStore) {
this.redisTokenStore = redisTokenStore;
}
} }
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.maxkey.authn.support.socialsignon.token;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.maxkey.constants.ConstsTimeInterval;
import org.maxkey.persistence.redis.RedisConnection;
import org.maxkey.persistence.redis.RedisConnectionFactory;
import java.util.concurrent.ConcurrentHashMap;
public class RedisTokenStore {
protected int validitySeconds = ConstsTimeInterval.ONE_MINUTE * 2;
private final ConcurrentHashMap<String, String> tokenStore = new ConcurrentHashMap<String, String>();
public RedisTokenStore() {
super();
}
public static String PREFIX = "REDIS_QRSCRAN_SERVICE_";
public void store(String token) {
tokenStore.put(PREFIX + token,"-1");
/* DateTime currentDateTime = new DateTime();
RedisConnection conn = connectionFactory.getConnection();
conn.getConn().setex(PREFIX + token, validitySeconds, "-1");
conn.close();*/
}
public boolean bindtoken(String token,String loginname) {
boolean flag = false;
try {
/* DateTime currentDateTime = new DateTime();
RedisConnection conn = connectionFactory.getConnection();
conn.getConn().setex(PREFIX + token, validitySeconds, loginname);
//conn.setexObject(PREFIX + token, validitySeconds, loginname);
conn.close();*/
tokenStore.put(PREFIX + token,loginname);
return true;
}catch (Exception e) {
}
return flag;
}
public String get(String token) {
/* RedisConnection conn = connectionFactory.getConnection();
String value = conn.get(PREFIX + token);
if(StringUtils.isNotEmpty(value) && !"-1".equalsIgnoreCase(value)) {
conn.delete(PREFIX + token);
return value;
}*/
String value = tokenStore.get(PREFIX + token);
if(StringUtils.isNotEmpty(value) && !"-1".equalsIgnoreCase(value)) {
tokenStore.remove(PREFIX + token);
return value;
}
return value;
}
}
...@@ -20,10 +20,14 @@ package org.maxkey.autoconfigure; ...@@ -20,10 +20,14 @@ package org.maxkey.autoconfigure;
import java.io.IOException; import java.io.IOException;
import org.maxkey.authn.support.socialsignon.service.JdbcSocialsAssociateService; import org.maxkey.authn.support.socialsignon.service.JdbcSocialsAssociateService;
import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService; import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService;
import org.maxkey.authn.support.socialsignon.token.RedisTokenStore;
import org.maxkey.constants.ConstsPersistence;
import org.maxkey.entity.SocialsProvider; import org.maxkey.entity.SocialsProvider;
import org.maxkey.persistence.redis.RedisConnectionFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -40,10 +44,17 @@ public class SocialSignOnAutoConfiguration implements InitializingBean { ...@@ -40,10 +44,17 @@ public class SocialSignOnAutoConfiguration implements InitializingBean {
@Bean(name = "socialSignOnProviderService") @Bean(name = "socialSignOnProviderService")
@ConditionalOnClass(SocialsProvider.class) @ConditionalOnClass(SocialsProvider.class)
public SocialSignOnProviderService socialSignOnProviderService( public SocialSignOnProviderService socialSignOnProviderService(
JdbcTemplate jdbcTemplate) throws IOException { @Value("${maxkey.server.persistence}") int persistence,
JdbcTemplate jdbcTemplate,
RedisConnectionFactory redisConnFactory
) throws IOException {
SocialSignOnProviderService socialSignOnProviderService = new SocialSignOnProviderService(jdbcTemplate); SocialSignOnProviderService socialSignOnProviderService = new SocialSignOnProviderService(jdbcTemplate);
//load default Social Providers from database //load default Social Providers from database
socialSignOnProviderService.loadSocials("1"); socialSignOnProviderService.loadSocials("1");
RedisTokenStore redisTokenStore = new RedisTokenStore();
socialSignOnProviderService.setRedisTokenStore(redisTokenStore);
_logger.debug("SocialSignOnProviderService inited."); _logger.debug("SocialSignOnProviderService inited.");
return socialSignOnProviderService; return socialSignOnProviderService;
} }
......
...@@ -123,7 +123,7 @@ export class SocialsProviderComponent implements OnInit { ...@@ -123,7 +123,7 @@ export class SocialsProviderComponent implements OnInit {
onAdd(e: MouseEvent): void { onAdd(e: MouseEvent): void {
e.preventDefault(); e.preventDefault();
const modal = this.modalService.create({ const modal = this.modalService.create({
//nzContent: SocialsProviderEditerComponent, //nzContent: SocialsProviderBindUserComponent,
nzViewContainerRef: this.viewContainerRef, nzViewContainerRef: this.viewContainerRef,
nzComponentParams: { nzComponentParams: {
isEdit: false, isEdit: false,
...@@ -143,7 +143,7 @@ export class SocialsProviderComponent implements OnInit { ...@@ -143,7 +143,7 @@ export class SocialsProviderComponent implements OnInit {
e.preventDefault(); e.preventDefault();
const modal = this.modalService.create({ const modal = this.modalService.create({
//nzContent: SocialsProviderEditerComponent, //nzContent: SocialsProviderBindUserComponent,
nzViewContainerRef: this.viewContainerRef, nzViewContainerRef: this.viewContainerRef,
nzComponentParams: { nzComponentParams: {
isEdit: true, isEdit: true,
......
...@@ -14,13 +14,14 @@ ...@@ -14,13 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { Inject, Optional, Component, OnInit } from '@angular/core'; import { Inject, Optional, Component, OnInit, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ReuseTabService } from '@delon/abc/reuse-tab'; import { ReuseTabService } from '@delon/abc/reuse-tab';
import { SettingsService } from '@delon/theme'; import { SettingsService } from '@delon/theme';
import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
import { AuthnService } from '../../service/authn.service'; import { AuthnService } from '../../service/authn.service';
import { SocialsProviderService } from '../../service/socials-provider.service'; import { SocialsProviderService } from '../../service/socials-provider.service';
import {SocialsProviderBindUserComponent} from "./socials-provider-bind-user/socials-provider-bind-user.component";
@Component({ @Component({
selector: 'app-callback', selector: 'app-callback',
...@@ -30,6 +31,8 @@ export class CallbackComponent implements OnInit { ...@@ -30,6 +31,8 @@ export class CallbackComponent implements OnInit {
provider = ''; provider = '';
constructor( constructor(
private viewContainerRef: ViewContainerRef,
private modalService: NzModalService,
private router: Router, private router: Router,
private socialsProviderService: SocialsProviderService, private socialsProviderService: SocialsProviderService,
private settingsService: SettingsService, private settingsService: SettingsService,
...@@ -50,6 +53,11 @@ export class CallbackComponent implements OnInit { ...@@ -50,6 +53,11 @@ export class CallbackComponent implements OnInit {
// 设置用户Token信息 // 设置用户Token信息
this.authnService.auth(res.data); this.authnService.auth(res.data);
} }
else if (res.code === 102) {
//绑定用户
this.openBindUser(res.message)
return;
}
this.authnService.navigate({}); this.authnService.navigate({});
}); });
} else { } else {
...@@ -60,4 +68,25 @@ export class CallbackComponent implements OnInit { ...@@ -60,4 +68,25 @@ export class CallbackComponent implements OnInit {
}); });
} }
} }
openBindUser(socialUserId: String) {
console.log("bind user : ",this.provider,socialUserId);
const modal = this.modalService.create({
nzContent: SocialsProviderBindUserComponent,
nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {
socialUserId: socialUserId,
provider: this.provider
},
nzOnOk: () => new Promise(resolve => setTimeout(resolve, 1000))
});
// Return a result when closed
modal.afterClose.subscribe(result => {
if (result.refresh) {
}
});
}
} }
...@@ -82,8 +82,11 @@ ...@@ -82,8 +82,11 @@
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</div> </div>
<div nz-row style="{{ loginType == 'qrscan' ? '' : 'display: none;' }}"> <div nz-row *ngIf="loginType=='qrscan'">
<div class="qrcode" id="div_qrcodelogin"> </div> <div class="qrcode" id="div_qrcodelogin" style="background: #fff;padding: 20px;"> </div>
<div id="qrexpire" *ngIf="qrexpire" style="width: 100%;text-align: center;background: #fff;padding-bottom: 20px;">
二维码过期 <a href="javascript:" (click)="getQrCode()">刷新</a>
</div>
</div> </div>
<nz-form-item *ngIf="loginType == 'normal' || loginType == 'mobile'"> <nz-form-item *ngIf="loginType == 'normal' || loginType == 'mobile'">
<nz-col [nzSpan]="12"> <nz-col [nzSpan]="12">
......
@import '@delon/theme/index'; @import '@delon/theme/index';
:host { :host {
display: block; display: block;
width: 368px; width: 368px;
...@@ -81,4 +82,4 @@ input{ ...@@ -81,4 +82,4 @@ input{
.qrcode iframe{ .qrcode iframe{
border: 0; border: 0;
} }
\ No newline at end of file
...@@ -31,6 +31,7 @@ import { ImageCaptchaService } from '../../../service/image-captcha.service'; ...@@ -31,6 +31,7 @@ import { ImageCaptchaService } from '../../../service/image-captcha.service';
import { SocialsProviderService } from '../../../service/socials-provider.service'; import { SocialsProviderService } from '../../../service/socials-provider.service';
import { CONSTS } from '../../../shared/consts'; import { CONSTS } from '../../../shared/consts';
import { stringify } from 'querystring'; import { stringify } from 'querystring';
@Component({ @Component({
...@@ -54,6 +55,7 @@ export class UserLoginComponent implements OnInit, OnDestroy { ...@@ -54,6 +55,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
loginType = 'normal'; loginType = 'normal';
loading = false; loading = false;
passwordVisible = false; passwordVisible = false;
qrexpire = false;
imageCaptcha = ''; imageCaptcha = '';
captchaType = ''; captchaType = '';
state = ''; state = '';
...@@ -287,6 +289,10 @@ export class UserLoginComponent implements OnInit, OnDestroy { ...@@ -287,6 +289,10 @@ export class UserLoginComponent implements OnInit, OnDestroy {
} }
getQrCode(): void { getQrCode(): void {
this.qrexpire = false;
if (this.interval$) {
clearInterval(this.interval$);
}
this.authnService.clearUser(); this.authnService.clearUser();
this.socialsProviderService.scanqrcode(this.socials.qrScan).subscribe(res => { this.socialsProviderService.scanqrcode(this.socials.qrScan).subscribe(res => {
if (res.code === 0) { if (res.code === 0) {
...@@ -294,11 +300,14 @@ export class UserLoginComponent implements OnInit, OnDestroy { ...@@ -294,11 +300,14 @@ export class UserLoginComponent implements OnInit, OnDestroy {
this.qrScanWorkweixin(res.data); this.qrScanWorkweixin(res.data);
} else if (this.socials.qrScan === 'dingtalk') { } else if (this.socials.qrScan === 'dingtalk') {
this.qrScanDingtalk(res.data); this.qrScanDingtalk(res.data);
} else if (this.socials.qrScan === 'feishu') { } else if (this.socials.qrScan === 'maxkey') {
this.qrScanMaxkey(res.data);
}else if (this.socials.qrScan === 'feishu') {
this.qrScanFeishu(res.data); this.qrScanFeishu(res.data);
} }
} }
}); });
} }
// #endregion // #endregion
...@@ -364,4 +373,46 @@ export class UserLoginComponent implements OnInit, OnDestroy { ...@@ -364,4 +373,46 @@ export class UserLoginComponent implements OnInit, OnDestroy {
}); });
} }
// #region QR Scan end // #region QR Scan end
qrScanMaxkey(data: any) {
// @ts-ignore
document.getElementById("div_qrcodelogin").innerHTML='';
// @ts-ignore
var qrcode = new QRCode("div_qrcodelogin", {
width: 200,
height: 200,
colorDark : "#000000",
colorLight : "#ffffff"
}).makeCode(data.state);
//3分钟监听二维码
this.count = 90;
this.interval$ = setInterval(() => {
this.count -= 1;
if(this.loginType != 'qrscan') {
clearInterval(this.interval$);
}
if (this.count <= 0) {
clearInterval(this.interval$);
}
//轮询发送监听请求
this.socialsProviderService.qrcallback(this.socials.qrScan,data.state).subscribe(res => {
if (res.code === 0) {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authnService.auth(res.data);
this.authnService.navigate({});
clearInterval(this.interval$);
} else if (res.code === 102) {
// 二维码过期
clearInterval(this.interval$);
this.qrexpire = true;
this.cdr.detectChanges();
} else if (res.code === 103) {
// 暂无用户扫码
}
});
this.cdr.detectChanges();
}, 2000);
}
} }
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { SharedModule } from '@shared'; import { SharedModule } from '@shared';
import { NzStepsModule } from 'ng-zorro-antd/steps'; import { NzStepsModule } from 'ng-zorro-antd/steps';
import { CallbackComponent } from './callback.component'; import { CallbackComponent } from './callback.component';
import { SocialsProviderBindUserComponent } from './socials-provider-bind-user/socials-provider-bind-user.component';
import { ForgotComponent } from './forgot/forgot.component'; import { ForgotComponent } from './forgot/forgot.component';
import { UserLockComponent } from './lock/lock.component'; import { UserLockComponent } from './lock/lock.component';
import { UserLoginComponent } from './login/login.component'; import { UserLoginComponent } from './login/login.component';
...@@ -26,7 +26,7 @@ import { PassportRoutingModule } from './passport-routing.module'; ...@@ -26,7 +26,7 @@ import { PassportRoutingModule } from './passport-routing.module';
import { UserRegisterResultComponent } from './register-result/register-result.component'; import { UserRegisterResultComponent } from './register-result/register-result.component';
import { UserRegisterComponent } from './register/register.component'; import { UserRegisterComponent } from './register/register.component';
const COMPONENTS = [UserLoginComponent, UserRegisterResultComponent, UserRegisterComponent, UserLockComponent, CallbackComponent]; const COMPONENTS = [SocialsProviderBindUserComponent,UserLoginComponent, UserRegisterResultComponent, UserRegisterComponent, UserLockComponent, CallbackComponent];
@NgModule({ @NgModule({
imports: [SharedModule, PassportRoutingModule, NzStepsModule], imports: [SharedModule, PassportRoutingModule, NzStepsModule],
......
<div *nzModalTitle>绑定</div>
<div>
<form nz-form [formGroup]="formGroup" (ngSubmit)="onSubmit($event)" se-container="1">
<nz-form-item style="width: 100%">
<nz-form-control nzErrorTip="Please input your telephone!">
<nz-input-group nzSize="large" nzPrefixIcon="user">
<input nz-input formControlName="telephone" placeholder="{{ 'mxk.login.text.mobile' | i18n }}" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-control nzErrorTip="Please input your verification code!">
<nz-input-group nzSize="large" nzPrefixIcon="mail" nzSearch [nzAddOnAfter]="suffixSendOtpCodeButton">
<input nz-input formControlName="verificationCode" placeholder="{{ 'mxk.login.text.captcha' | i18n }}" />
</nz-input-group>
<ng-template #suffixSendOtpCodeButton>
<button type="button" nz-button nzSize="large" (click)="sendOtpCode()" [disabled]="count > 0" nzBlock [nzLoading]="loading">
{{ count ? count + 's' : ('app.register.get-verification-code' | i18n) }}
</button>
</ng-template>
</nz-form-control>
</nz-form-item>
</form>
</div>
<div *nzModalFooter>
<button nz-button nzType="default" (click)="onClose($event)">{{ 'mxk.text.close' | i18n }}</button>
<button nz-button nzType="primary" (click)="onSubmit($event)">{{ 'mxk.text.submit' | i18n }}</button>
</div>
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SocialsProviderBindUserComponent } from './socials-provider-bind-user.component';
describe('SocialsProviderBindUserComponent', () => {
let component: SocialsProviderBindUserComponent;
let fixture: ComponentFixture<SocialsProviderBindUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SocialsProviderBindUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SocialsProviderBindUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ChangeDetectorRef, Input, OnInit, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { I18NService } from '@core';
import { _HttpClient, ALAIN_I18N_TOKEN, SettingsService } from '@delon/theme';
import format from 'date-fns/format';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
import { AuthnService } from "../../../service/authn.service";
import { SocialsProviderService } from "../../../service/socials-provider.service";
import { ReuseTabService } from "@delon/abc/reuse-tab";
@Component({
selector: 'app-socials-provider-binduser',
templateUrl: './socials-provider-bind-user.component.html',
styles: [
`
nz-form-item {
width: 100%;
}
`
],
styleUrls: ['./socials-provider-bind-user.component.less']
})
export class SocialsProviderBindUserComponent implements OnInit {
@Input() socialUserId?: String;
@Input() provider?: String;
loading = false;
count = 0;
formGroup: FormGroup = new FormGroup({});
interval$: any;
constructor(
private modalRef: NzModalRef,
private fb: FormBuilder,
private authnService: AuthnService,
private msg: NzMessageService,
private socialsProviderService: SocialsProviderService,
@Inject(ALAIN_I18N_TOKEN) private i18n: I18NService,
@Inject(ReuseTabService)
private reuseTabService: ReuseTabService,
private cdr: ChangeDetectorRef
) {}
ngOnInit(): void {
this.formGroup = this.fb.group({
telephone: [null, [Validators.required]],
verificationCode: [null, [Validators.required]]
});
console.log("bind open form : ",this.provider,this.socialUserId)
}
onClose(e: MouseEvent): void {
e.preventDefault();
this.modalRef.destroy({ refresh: false });
}
onSubmit(e: MouseEvent): void {
console.log("this.formGroup.valid",this.formGroup.valid)
//表单验证
if (this.formGroup.valid) {
let request = {
username: this.socialUserId,
mobile: this.formGroup.get('telephone')?.value,
code: this.formGroup.get('verificationCode')?.value,
authType: this.provider
}
this.authnService.bindSocialsUser(request).subscribe(res => {
if (res.code === 0) {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authnService.auth(res.data);
this.authnService.navigate({});
} else {
this.msg.error(`绑定失败`);
}
});
} else {
Object.values(this.formGroup.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
e.preventDefault();
}
sendOtpCode(): void {
this.authnService.produceOtp({ mobile:this.formGroup.get('telephone')?.value}).subscribe(res => {
if (res.code !== 0) {
this.msg.error(`发送失败`);
}else {
this.msg.success(`发送成功`);
}
});
this.count = 59;
this.interval$ = setInterval(() => {
this.count -= 1;
if (this.count <= 0) {
clearInterval(this.interval$);
}
this.cdr.detectChanges();
}, 1000);
}
}
...@@ -61,6 +61,10 @@ export class AuthnService { ...@@ -61,6 +61,10 @@ export class AuthnService {
return this.http.post('/login/signin?_allow_anonymous=true', authParam); return this.http.post('/login/signin?_allow_anonymous=true', authParam);
} }
bindSocialsUser(authParam: any) {
return this.http.post('/login/signin/bindusersocials?_allow_anonymous=true', authParam);
}
//退出 //退出
logout() { logout() {
this.cookieService.delete(CONSTS.CONGRESS, '/'); this.cookieService.delete(CONSTS.CONGRESS, '/');
......
...@@ -43,7 +43,15 @@ export class SocialsProviderService extends BaseService<SocialsProvider> { ...@@ -43,7 +43,15 @@ export class SocialsProviderService extends BaseService<SocialsProvider> {
return this.getByParams(params, `/logon/oauth20/callback/${provider}?_allow_anonymous=true`); return this.getByParams(params, `/logon/oauth20/callback/${provider}?_allow_anonymous=true`);
} }
bindUser(provider: string, params: NzSafeAny): Observable<Message<SocialsProvider>> {
return this.getByParams(params, `/logon/oauth20/binduser/${provider}?_allow_anonymous=true`);
}
bind(provider: string, params: NzSafeAny): Observable<Message<SocialsProvider>> { bind(provider: string, params: NzSafeAny): Observable<Message<SocialsProvider>> {
return this.getByParams(params, `/logon/oauth20/bind/${provider}?_allow_anonymous=true`); return this.getByParams(params, `/logon/oauth20/bind/${provider}?_allow_anonymous=true`);
} }
qrcallback(provider: string, token: string ): Observable<Message<SocialsProvider>> {
return this.getByParams({}, `/logon/oauth20/qrcallback/${provider}/${token}?_allow_anonymous=true`);
}
} }
...@@ -27,7 +27,7 @@ export const environment = { ...@@ -27,7 +27,7 @@ export const environment = {
production: false, production: false,
useHash: true, useHash: true,
api: { api: {
baseUrl: 'http://sso.maxkey.top:9527/sign/', baseUrl: '/sign/',
refreshTokenEnabled: true, refreshTokenEnabled: true,
refreshTokenType: 're-request' refreshTokenType: 're-request'
}, },
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
<meta http-equiv="description" content="MaxKey Single Sign-On"> <meta http-equiv="description" content="MaxKey Single Sign-On">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<script src="./assets/transform.js"></script> <script src="./assets/transform.js"></script>
<script src="./assets/qrcode/qrcode.min.js"></script>
<!-- Apple Touch Icon --> <!-- Apple Touch Icon -->
<!-- <link rel="apple-touch-icon" href="custom-icon.png"> --> <!-- <link rel="apple-touch-icon" href="custom-icon.png"> -->
<style type="text/css"> <style type="text/css">
...@@ -142,9 +143,9 @@ ...@@ -142,9 +143,9 @@
</div> </div>
</body> </body>
<!--attention http or https--> <!--attention http or https-->
<!--企业微信 --企业微信
<script src="http://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js"></script> <script src="http://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js"></script>
-->
<!--钉钉--> <!--钉钉-->
<!----> <!---->
<script src="http://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script> <script src="http://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
...@@ -191,4 +192,4 @@ ...@@ -191,4 +192,4 @@
</script> </script>
--> -->
</html> </html>
\ No newline at end of file
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
//springboot jar
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id 'java'
id "io.spring.dependency-management" version "1.0.11.RELEASE"
id 'org.springframework.boot' version "${springBootVersion}"
}
apply plugin: 'io.spring.dependency-management'
description = "maxkey-web-maxkey" description = "maxkey-web-maxkey"
//add support for Java bootJar {
apply plugin: 'java' dependsOn jar
baseName = 'maxkey-boot'
version = "${project.version}-ga"
mainClass = 'org.maxkey.MaxKeyApplication'
manifest {
attributes(
"Implementation-Title": project.name,
"Implementation-Vendor": project.vendor,
"Created-By": project.author,
"Implementation-Date": java.time.ZonedDateTime.now(),
"Implementation-Version": project.version
)
}
}
dependencies { dependencies {
implementation project(":maxkey-common") implementation project(":maxkey-common")
...@@ -12,8 +43,8 @@ dependencies { ...@@ -12,8 +43,8 @@ dependencies {
implementation project(":maxkey-authentications:maxkey-authentication-social") implementation project(":maxkey-authentications:maxkey-authentication-social")
implementation project(":maxkey-authentications:maxkey-authentication-captcha") implementation project(":maxkey-authentications:maxkey-authentication-captcha")
implementation project(":maxkey-authentications:maxkey-authentication-otp") implementation project(":maxkey-authentications:maxkey-authentication-otp")
implementation project(":maxkey-authentications:maxkey-authentication-provider") implementation project(":maxkey-authentications:maxkey-authentication-provider")
implementation project(":maxkey-authentications:maxkey-authentication-sms") implementation project(":maxkey-authentications:maxkey-authentication-sms")
implementation project(":maxkey-protocols:maxkey-protocol-authorize") implementation project(":maxkey-protocols:maxkey-protocol-authorize")
implementation project(":maxkey-protocols:maxkey-protocol-cas") implementation project(":maxkey-protocols:maxkey-protocol-cas")
...@@ -23,5 +54,4 @@ dependencies { ...@@ -23,5 +54,4 @@ dependencies {
implementation project(":maxkey-protocols:maxkey-protocol-oauth-2.0") implementation project(":maxkey-protocols:maxkey-protocol-oauth-2.0")
implementation project(":maxkey-protocols:maxkey-protocol-saml-2.0") implementation project(":maxkey-protocols:maxkey-protocol-saml-2.0")
implementation project(":maxkey-protocols:maxkey-protocol-jwt") implementation project(":maxkey-protocols:maxkey-protocol-jwt")
}
} \ No newline at end of file
...@@ -34,12 +34,11 @@ import org.maxkey.authn.support.rememberme.AbstractRemeberMeManager; ...@@ -34,12 +34,11 @@ import org.maxkey.authn.support.rememberme.AbstractRemeberMeManager;
import org.maxkey.authn.support.rememberme.RemeberMe; import org.maxkey.authn.support.rememberme.RemeberMe;
import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService; import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService;
import org.maxkey.configuration.ApplicationConfig; import org.maxkey.configuration.ApplicationConfig;
import org.maxkey.entity.Institutions; import org.maxkey.constants.ConstsLoginType;
import org.maxkey.entity.Message; import org.maxkey.entity.*;
import org.maxkey.entity.UserInfo;
import org.maxkey.password.onetimepwd.AbstractOtpAuthn; import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
import org.maxkey.password.onetimepwd.MailOtpAuthnService;
import org.maxkey.password.sms.SmsOtpAuthnService; import org.maxkey.password.sms.SmsOtpAuthnService;
import org.maxkey.persistence.service.SocialsAssociatesService;
import org.maxkey.persistence.service.UserInfoService; import org.maxkey.persistence.service.UserInfoService;
import org.maxkey.web.WebConstants; import org.maxkey.web.WebConstants;
import org.maxkey.web.WebContext; import org.maxkey.web.WebContext;
...@@ -81,6 +80,9 @@ public class LoginEntryPoint { ...@@ -81,6 +80,9 @@ public class LoginEntryPoint {
@Autowired @Autowired
SocialSignOnProviderService socialSignOnProviderService; SocialSignOnProviderService socialSignOnProviderService;
@Autowired
SocialsAssociatesService socialsAssociatesService;
@Autowired @Autowired
KerberosService kerberosService; KerberosService kerberosService;
...@@ -165,6 +167,47 @@ public class LoginEntryPoint { ...@@ -165,6 +167,47 @@ public class LoginEntryPoint {
return new Message<AuthJwt>(Message.FAIL).buildResponse(); return new Message<AuthJwt>(Message.FAIL).buildResponse();
} }
@RequestMapping(value={"/signin/bindusersocials"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> bindusersocials(@RequestBody LoginCredential credential) {
//短信验证码
String code = credential.getCode();
//映射社交服务的账号
String username = credential.getUsername();
//maxkey存储的手机号
String mobile = credential.getMobile();
//社交服务类型
String authType = credential.getAuthType();
UserInfo userInfo = userInfoService.findByEmailMobile(mobile);
//验证码验证是否合法
if (smsAuthnService.getByInstId(WebContext.getInst().getId()).validate(userInfo,code)) {
//合法进行用户绑定
SocialsAssociate socialsAssociate = new SocialsAssociate();
socialsAssociate.setUserId(userInfo.getId());
socialsAssociate.setUsername(userInfo.getUsername());
socialsAssociate.setProvider(authType);
socialsAssociate.setSocialUserId(username);
socialsAssociate.setInstId(userInfo.getInstId());
//插入Maxkey和社交服务的用户映射表
socialsAssociatesService.insert(socialsAssociate);
//设置完成后,进行登录认证
LoginCredential loginCredential =new LoginCredential(
socialsAssociate.getUsername(),"", ConstsLoginType.SOCIALSIGNON);
SocialsProvider socialSignOnProvider = socialSignOnProviderService.get(socialsAssociate.getInstId(),socialsAssociate.getProvider());
loginCredential.setProvider(socialSignOnProvider.getProviderName());
Authentication authentication = authenticationProvider.authenticate(loginCredential,true);
return new Message<AuthJwt>(authTokenService.genAuthJwt(authentication)).buildResponse();
}
return new Message<AuthJwt>(Message.FAIL).buildResponse();
}
/** /**
* normal * normal
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册