注:このブログではXiaobaiの執筆に関するアイデアを紹介しています。以下のコンテンツとサードパーティのジャンプ環境は主にYu Shengjunから提供されています。
1. アイデア
1。ログインページで、使用可能な共同ログインインターフェース(サードパーティのログインアバター、 requestAddressを含む)をバックグラウンドで照会します。
2.アバターをクリックしてQRコードスキャンインターフェイスに入ります
3. QQコードをスキャンした後、requestAddressのコールバックアドレスに従って、バックグラウンドのカスタムログインコールバックメソッドunionLoginCallbackを入力します
4.コールバックメソッドでユーザーのopenIdを取得し、アセンブリ後に定義されたフロントエンドアソシエーションページに戻ります(openIdに従ってユーザーが関連付けられているかどうかを照会し、アソシエーションが渡された場合、ユーザー情報は関連付けられたアカウントページに転送され、ユーザーはクリックすると直接ホームページにジャンプします。接続されていません)
5. [既存のアカウントに関連付ける]をクリックして、関連付けられているアカウントページに移動し、ユーザーのopenIdをページに渡します。
6.ユーザーは、関連付けられたアカウントページでプラットフォームのログインアカウントパスワードを入力し、ユーザーのopenIdをバックグラウンドに一緒に渡します。呼び出しメソッドは、プラットフォームのログインインターフェイスです。入力したアカウントパスワードが正しければ、ユーザーのopenIdが追加されますユーザーに対応するデータベースデータに移動し、ホームページにジャンプします。
第二に、開発コード
(1)データベースデータ
/ * Navicat Premium Data Transfer ソースサーバー:localhost ソースサーバータイプ:MySQL ソースサーバーバージョン:50647 ソースホスト:localhost:3306 ソーススキーマ:cyb ターゲットサーバータイプ:MySQL ターゲットサーバーバージョン:50647 ファイルエンコーディング:65001 日付:12/04 / 2020 02:42:17 * / セット名utf8mb4; SET FOREIGN_KEY_CHECKS = 0; ------------------------------ meite_userのテーブル構造 ------------- ---------------- DROP TABLE IF EXISTS `meite_user`; CREATE TABLE `meite_user`( ` USER_ID` int(12)NOT NULL AUTO_INCREMENT COMMENT 'user_id'、 `MOBILE` varchar(11)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Mobile Number'、 ` PASSWORD` varchar(64)CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Password'、 `USER_NAME` varchar(50)CHARACTER utf8_general_ci NULL DEFAULT NULL COMMENT 'user name'、 `SEX` tinyint(1)NULL DEFAULT 0 COMMENT 'sex 1 male 2 female'、 ` AGE` tinyint(3)NULL DEFAULT 0 COMMENT 'age'、 `CREATE_TIME` timestamp(0 )NULL DEFAULT NULL COMMENT '登録時間'、 `IS_AVALIBLE` tinyint(1)NULL DEFAULT 1 COMMENT 'Available 1 normal 2 frozen'、 ` PIC_IMG` varchar(255)CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'ユーザーアバター' 、 `QQ_OPENID` varchar(50)CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'QQ共同ログインID'、 `WX_OPENID` varchar(50)CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '微信公众号关注id'、 PRIMARY KEY( `USER_ID`)USING BTREE、 UNIQUE INDEX` MOBILE_UNIQUE`( `MOBILE`)USING BTREE )ENGINE = InnoDB AUTO_INCREMENT = 87 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户会员表' ROW_FORMAT ------------------------------ meite_userのレコード -------------- --------------- INSERT INTO `meite_user` VALUES(86、 '18774833827'、 'E10ADC3949BA59ABBE56E057F20F883E'、 '1'、1、0、 '2020-03-15 22:34: 45 '、0、' 1 '、' 3EAB229E0EAAB047174224A5845B224E '、' oOX38w3WD3JUjL5ORcr4OADNqfSw '); SET FOREIGN_KEY_CHECKS = 1;
(2)共同ログインインターフェース
import com.cyb.base.BaseResponse; import com.cyb.member.api.dto.resp.UnionLoginDto; import io.swagger.annotations.Api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Api(tags = "联合登陆 インタフェース")パブリックインターフェースMemberUnionLoginService { / ** *に基づいて異なる別の联合登陆ID * * @param unionPublicId * @return * / @GetMapping( "/ unionLogin") BaseResponse <String> unionLogin(@ RequestParam( "unionPublicId")String unionPublicId); / ** *共同ログインコールバックインターフェース * * @return * / @GetMapping( "/ login / oauth / callback") String unionLoginCallback(@RequestParam( "unionPublicId")String unionPublicId); / ** *查询当前开通的溝道 * * @return * / @GetMapping( "/ unionLoginList") @ ResponseBody BaseResponse <List <UnionLoginDto >> unionLoginList(); }
(3)共同ログイン実装クラス
import com.alibaba.fastjson.JSONObject; import com.cyb.base.BaseApiService; import com.cyb.base.BaseResponse; import com.cyb.bean.CybBeanUtils; import com.cyb.member.api.dto.resp.UnionLoginDto; import com.cyb.member.api.service.MemberUnionLoginService; import com.cyb.member.impl.entitydo.UnionLoginDo; import com.cyb.member.impl.mapper.UnionLoginMapper; import com.cyb.member.impl.strategy.UnionLoginStrategy; import com.cyb.utils.SpringContextUtils; import com.cyb.utils.TokenUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.List; // @ RestController @Controller @CrossOrigin public class MemberUnionLoginServiceImpl extends BaseApiService implements MemberUnionLoginService { @Autowired private UnionLoginMapper unionLoginMapper; @Autowired プライベートTokenUtils tokenUtils; @Value( "$ {cyb.login.vue.bindingurl}") プライベート文字列bindingurl; @オーバーライド public BaseResponse <String> unionLogin(String unionPublicId){ if(StringUtils.isEmpty(unionPublicId)){ return setResultError( "unionPublicId cannot be empty"); } //チャネルIDに基づいてユニオンの基本情報をクエリします UnionLoginDo unionLoginDo = unionLoginMapper.selectByUnionLoginId( ); if(unionLoginDo == null){ return setResultError( "チャネルが閉じているか、存在しない可能性があります"); } String state = tokenUtils.createToken( "member.unionLogin"、 ""); @Override 文字列requestAddres = unionLoginDo.getRequestAddress()+ "&state =" + state; JSONObject dataObjects = new JSONObject(); dataObjects.put( "requestAddres"、requestAddres); return setResultSuccess(dataObjects); } public String unionLoginCallback(String unionPublicId){ //チャネルIDに基づいて基本的なユニオン情報をクエリする UnionLoginDo unionLoginDo = unionLoginMapper.selectByUnionLoginId( unionPublicId); String unionBeanId = unionLoginDo.getUnionBeanId(); //ビーニーに基づいてSpringコンテナからストラテジークラスを検索します UnionLoginStrategy unionLoginStrategy = SpringContextUtils.getBean(unionBeanId、UnionLoginStrategy.class); //現在のスレッドに従ってリクエストオブジェクト HttpServletRequestリクエストを取得します=((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes()))。getRequest(); String openId = unionLoginStrategy.unionLoginCallback(request、unionLoginDo); JSONObject jsonObject = new JSONObject(); jsonObject.put( "openId"、openId); jsonObject.put( "unionPublicId"、unionPublicId); String openToken = List <UnionLoginDo> unionLoginList = unionLoginMapper.selectByUnionLoginList(); tokenUtils.createToken( "mayikt.unionLogin。"、jsonObject.toJSONString()); "redirect:" + bindingurl + openTokenを返します。 } @Override public BaseResponse <List <UnionLoginDto >> unionLoginList(){ if(unionLoginList == null){ return setResultError( "当前没有可用導管道"); } List <UnionLoginDto> unionLoginDtos = CybBeanUtils.doToDtoList(unionLoginList、UnionLoginDto.class); setResultSuccess(unionLoginDtos);を返します。 } }
(4)共同ログイン戦略モードのインターフェース
/ * *共同ログインコールバック * @Author陈远波 * @Date 2020-04-12 * @param null * @return * / String unionLoginCallback(HttpServletRequest request、UnionLoginDo unionLoginDo); / * *ユーザーのopenIDに基づいてチャネル情報を取得 * @Author陈远波 * @Date 2020-04-12 * @param openId user openID * @return * / UserDo getDbOpenId(String openId); / * *ユーザーIDに応じてユーザーのopenIDを変更する * @Author陈远波 * @Date 2020-04-12 * @ param userId user id * @param openId user openId * @return * / int updateUseOpenId(Long userId、String openId); }
(E)QQ 共同ログイン戦略モデル実装クラス
@Component パブリッククラスQQUnionLoginStrategyは、UnionLoginStrategy { @Value( "$ {cyb.login.qq.accesstoken}") private String qqAccessTokenAddres;を実装します。 @Value( "$ {cyb.login.qq.openid}") プライベート文字列qqOpenIdAddres; @Autowired プライベートUserMapper userMapper; @Override public String unionLoginCallback(HttpServletRequest request、UnionLoginDo unionLoginDo){ String code = request.getParameter( "code"); if(StringUtils.isEmpty(code)){ nullを返す; } // 1。根拠授权码获取accessToken // 1.根拠授权码获取accessToken 文字列newQQAccessTokenAddres = qqAccessTokenAddres.replace( "{client_id}" } 、unionLoginDo.getAppId())。replace( "{client_secret}"、unionLoginDo.getAppKey())。 replace( "{code}"、code).replace( "{redirect_uri}"、unionLoginDo.getRedirectUri()); 文字列resultAccessToken = HttpClientUtils.httpGetResultString(newQQAccessTokenAddres); boolean contains = resultAccessToken.contains( "access_token ="); if(!contains){ return null; } String [] split = resultAccessToken.split( "="); 文字列accessToken = split [1]; if(StringUtils.isEmpty(accessToken)){ nullを返す; // 2. accessToken获取用户的openidに基づく 文字列resultQQOpenId = HttpClientUtils.httpGetResultString(qqOpenIdAddres + accessToken); if(StringUtils.isEmpty(resultQQOpenId)){ nullを返します。 } boolean openid = resultQQOpenId.contains( "openid"); if(!openid){ nullを返す; } 文字列配列[] = resultQQOpenId.replace( "callback({"、 "").replace( "});"、 "").replace( "\" "、" ").trim()。split(" : "); String openId = array [2]; return openId; return userMapper.selectByQQOpenId(openId); } @Override public int updateUseOpenId(Long userId、String openId){ return userMapper.updateUserOpenId(userId、openId); } }
(6)ログインインターフェース
@RestController @Api(tags = "Member Login Service") パブリックインターフェイスMemberLoginService { / * * * @Author陈远波 * @Date 2020-03-25 * @param @RequestHeader( "X-Real-IP")nginxからのリクエストヘッダーブラウザの実際のIPを 取得します* * RequestHeader( "channel")リクエストヘッダーからログインソースPCを取得します、Android、iOS * @return * / @PostMapping( "/ login") @ApiOperation(value = "Member Login"、notes = "シリアル化のパラメーターを受信する") BaseResponse <JSONObject> login(@RequestBody UserLoginDto userLoginDto、@RequestHeader( "X-Real-IP") String sourceIp、@RequestHeader( "channel")String channel、@RequestHeader( "deviceInfor" )String deviceInfor); }
ゴードン記録インターフェース
@RestController @ Slf4j @CrossOrigin public Class MemberLoginServiceImpl extends BaseApiService implements MemberLoginService { @Autowired private UserMapper userMapper; @Autowired プライベートTokenUtils tokenUtils; @Value( "$ {cyb.login.token.prefix}") private String loginTokenPrefix; @Autowired プライベートAsyncLoginLogManage asyncLoginLogManage; @Autowired プライベートUserLoginLogMapper userLoginLogMapper; @Autowired プライベートChannelUtils channelUtils; @Override public BaseResponse <JSONObject> login(UserLoginDto userLoginDto、String sourceIp 、文字列チャネル、文字列deviceInfor){ //パラメータ验证 String mobile = userLoginDto.getMobile(); if(StringUtils.isEmpty(mobile)){ return setResultError( "mobile parameter cannot be empty"); } String passWord = userLoginDto.getPassWord (); if(StringUtils.isEmpty(userLoginDto.getPassWord())){ return setResultError( "passWord parameter cannot be empty"); } if(!channelUtils.existChannel(channel)){ return setResultError( "Login type error! "); } //データベースにクエリを実行します String newPassWord = MD5Util.MD5(passWord); UserDo loginUserDo = userMapper.login(mobile、newPassWord); if(loginUserDo == null){ log.info(Thread.currentThread().GetName()+ "Processing Process 1"); return setResultError( "携帯電話番号またはパスワードが正しくありません!"); } //デバイス情報 if(StringUtils.isEmpty(deviceInfor)){ return setResultError( "设备信息不可能にする空!"); } // 获取userId Long userId = loginUserDo.getUserId(); String userToken = tokenUtils.createToken(loginTokenPrefix、userId + ""); JSONObject resultJSON = new JSONObject(); resultJSON.put( "userToken"、userToken); 文字列wxOpenId = loginUserDo.getWxOpenId(); String openIdToken = userLoginDto.getOpenIdToken(); //写入日志 asyncLoginLogManage.loginLog(openIdToken、wxOpenId、mobile、userId、sourceIp、new Date()、userToken 、channel、deviceInfor); log.info(Thread.currentThread()。getName()+ "处理流程3"); setResultSuccess(resultJSON);を返します。 } public void loginLog(Long userId、String loginIp、Date loginTime、String loginToken、String channel、 String equipment){ UserLoginLogDo userLoginLogDo = new UserLoginLogDo(userId、loginIp、loginTime、loginToken、channel、equipment); log.info(Thread.currentThread()。getName()+ "、userLoginLogDo:" + userLoginLogDo.toString()+ "、流程2"); userLoginLogMapper.insertUserLoginLog(userLoginLogDo); log.info(Thread.currentThread()。getName()+ "处理流程2"); } }
(7)設定ファイル
cyb: login: token: prefix:memberlogin channel:pc、android、ios qq: accesstoken:https : //graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id={client_id}&client_secret={client_secret}&code= {code}&redirect_uri = {redirect_uri} openid:https : //graph.qq.com/oauth2.0/me?access_token = wx:accesstoken:https : //api.weixin.qq.com/sns/oauth2/access_token? appid = APPID&secret = SECRET&code = CODE&grant_type = authorization_code vue: bindingurl:http : //127.0.0.1:8849 /mayikt_mt_shop/relation_login.html?openIdToken=
3. 注意が必要な事項:
このプロセスでは、クロスドメインエラーが発生します
多くの解決策がありますが、ここではコメントの形式を使用して解決します。具体的な解決策は次のとおりです。
1. 応答ヘッダーに設定して、小規模企業にのみ適したクロスドメインを許可する
响应配置 response.setHeader( "Access-Control-Allow-Origin"、 "*");
2. HttpClient を使用した低い転送効率
3. 使用JSONP 処理、JSON P- 最大の欠点支援のGET 要求はサポートされていないポストの要求を
4. nginxを使用して、ブラウザーによってアクセスされるプロジェクトのドメイン名またはポート番号とインターフェースプロジェクトが一致するように構成します。
www.mayikt.com/vue forward to vue プロジェクト
www.mayikt.com/api がインターフェースプロジェクトに転送されました
5. nginxで直接クロスドメインを許可するコードを設定できます
"Access-Control-Allow-Origin"、 "*"
6. ゲートウェイは、クロスドメインを許可するためにnginxと同様のコードで構成することもできます
"Access-Control-Allow-Origin"、 "*"
7. SpringBoot アノテーションを使用して、クロスドメインの問題@CrossOrigin を解決する
8. マイクロサービスゲートウェイを使用して、ブラウザーがアクセスするアイテムとインターフェースアイテムのドメイン名またはポート番号の整合性を設定することもできます。
第四に、エフェクト表示
1.ホームページにログインします
2.サードパーティのログインをクリックして、バーコードスキャンインターフェイスにジャンプします。
コードスキャン時の携帯電話インターフェース
確認関連付けインターフェイスに入る
関連付けるログインアカウントのパスワードを入力してください
上記の内容について質問がある場合は、メッセージに注意を払うことができます。ソースを指定してください