feat(登录): 新增通过token快速登录功能

新增SsoLoginUserinfo类用于存储登录用户信息,并在WxLoginController中实现tokenLogin接口,支持通过token快速登录。同时优化了ShopController中的微信授权逻辑,支持传递token参数。
This commit is contained in:
dzq 2025-04-18 08:20:21 +08:00
parent 41dc6aee69
commit ea22f44e41
4 changed files with 171 additions and 10 deletions

View File

@ -1,5 +1,7 @@
package com.agileboot.api.controller; package com.agileboot.api.controller;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.api.response.ShopGoodsResponse; import com.agileboot.api.response.ShopGoodsResponse;
import com.agileboot.common.constant.WeixinConstants; import com.agileboot.common.constant.WeixinConstants;
import com.agileboot.common.core.dto.ResponseDTO; import com.agileboot.common.core.dto.ResponseDTO;
@ -10,15 +12,17 @@ import com.agileboot.domain.shop.goods.GoodsApplicationService;
import com.agileboot.domain.shop.goods.db.SearchGoodsWithCabinetDO; import com.agileboot.domain.shop.goods.db.SearchGoodsWithCabinetDO;
import com.agileboot.domain.shop.goods.db.ShopGoodsEntity; import com.agileboot.domain.shop.goods.db.ShopGoodsEntity;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.CrossOrigin; import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView; import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
@ -26,6 +30,7 @@ import org.springframework.web.util.UriComponentsBuilder;
@RestController @RestController
@RequestMapping("/api/shop") @RequestMapping("/api/shop")
@CrossOrigin(origins = "*", allowedHeaders = "*") @CrossOrigin(origins = "*", allowedHeaders = "*")
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class ShopController { public class ShopController {
@ -62,18 +67,41 @@ public class ShopController {
} }
@GetMapping("/wechatAuth") @GetMapping("/wechatAuth")
public RedirectView wechatAuthRedirect() { public RedirectView wechatAuthRedirect(HttpServletRequest request, @RequestParam String token) {
/*java.util.StringJoiner joiner = new java.util.StringJoiner("&");
request.getParameterMap().forEach((key, values) -> {
joiner.add(key + "=" + String.join(",", values));
});
log.info("wechatAuth 参数:{}", joiner.toString());*/
String state = "";
if (StringUtils.isNoneBlank(token)) {
state = "token_" + token;
}
/*try {
state = URLEncoder.encode(state, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
log.error("wechatAuth 编码失败", e);
}*/
String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize" String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize"
+ "?appid=" + WeixinConstants.appid + "?appid=" + WeixinConstants.appid
+ "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop" + "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop"
+ "&response_type=code" + "&response_type=code"
+ "&scope=snsapi_base" + "&scope=snsapi_base"
+ "&state=STATE#wechat_redirect"; + "&state=" + state
+ "#wechat_redirect";
return new RedirectView(authUrl); return new RedirectView(authUrl);
} }
@GetMapping("/qy/wechatAuth") @GetMapping("/qy/wechatAuth")
public RedirectView qyWechatAuthRedirect() { public RedirectView qyWechatAuthRedirect(HttpServletRequest request) {
/*java.util.StringJoiner joiner = new java.util.StringJoiner("&");
request.getParameterMap().forEach((key, values) -> {
joiner.add(key + "=" + String.join(",", values));
});
log.info("/qy/wechatAuth 参数:{}", joiner.toString());*/
String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize" String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize"
+ "?appid=" + WeixinConstants.corpid + "?appid=" + WeixinConstants.corpid
+ "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop-api%2Fapi%2Fshop%2FapprovalRedirect" + "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop-api%2Fapi%2Fshop%2FapprovalRedirect"
@ -100,4 +128,9 @@ public class ShopController {
return new RedirectView(builder.build().encode().toUriString()); return new RedirectView(builder.build().encode().toUriString());
} }
public static void main(String[] args) throws UnsupportedEncodingException {
String res = URLEncoder.encode("token=12312", StandardCharsets.UTF_8.name());
System.out.println(res);
}
} }

View File

@ -2,15 +2,19 @@ package com.agileboot.api.controller;
import com.agileboot.common.core.dto.ResponseDTO; import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException; import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.api.SsoLoginUserinfo;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import com.agileboot.domain.ab98.api.Ab98ApiUtil; import com.agileboot.domain.ab98.api.Ab98ApiUtil;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import java.util.Map; import java.util.Map;
@Slf4j
@RestController @RestController
@RequestMapping("/api/wx/login") @RequestMapping("/api/wx/login")
@CrossOrigin(origins = "*", allowedHeaders = "*") @CrossOrigin(origins = "*", allowedHeaders = "*")
@ -88,7 +92,28 @@ public class WxLoginController {
data.setTel(loginResponse.getOutputData().getTel()); data.setTel(loginResponse.getOutputData().getTel());
return ResponseDTO.ok(data); return ResponseDTO.ok(data);
} catch (ApiException e) { } catch (ApiException e) {
log.error("短信验证失败: {}", e.getMessage());
return ResponseDTO.fail(e); return ResponseDTO.fail(e);
} }
} }
@GetMapping("/tokenLogin")
@ApiOperation(value = "通过token登录", notes = "用于快速登录,无需验证码")
public ResponseDTO<Ab98ApiUtil.LoginData> tokenLogin(@RequestParam String token) {
SsoLoginUserinfo loginUserinfo = Ab98ApiUtil.getLoginUserInfoRemote("wxshop", "34164e41f0c6694be6bbbba0dc50c14a", token, "");
Ab98ApiUtil.LoginData data = new Ab98ApiUtil.LoginData();
if (loginUserinfo == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.FAILED, "登录失败,请重新尝试"));
}
data.setFace_img(loginUserinfo.getFace());
data.setSuccess(true);
data.setSex(loginUserinfo.getSex());
data.setName(loginUserinfo.getName());
data.setUserid(String.valueOf(loginUserinfo.getId()));
data.setRegistered(true);
data.setTel(loginUserinfo.getPhone());
return ResponseDTO.ok(data);
}
} }

View File

@ -1,11 +1,15 @@
package com.agileboot.domain.ab98.api; package com.agileboot.domain.ab98.api;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.agileboot.common.exception.ApiException; import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode; import com.agileboot.common.exception.error.ErrorCode;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -22,9 +26,6 @@ public class Ab98ApiUtil {
* 发送短信验证码短信登录 * 发送短信验证码短信登录
* @param token 通过getToken获取的临时令牌有效期5分钟 * @param token 通过getToken获取的临时令牌有效期5分钟
* @param tel 接收验证码的手机号码需符合格式11位数字 * @param tel 接收验证码的手机号码需符合格式11位数字
* @param nobind "true"表示不绑定手机号固定值
* @param for_login "true"表示用于登录固定值
* @param from 来源渠道固定值"jt"
*/ */
public static SmsSendResponse sendLoginSms(String token, String tel) { public static SmsSendResponse sendLoginSms(String token, String tel) {
String url = BASE_URL + "?code=doSendSms"; String url = BASE_URL + "?code=doSendSms";
@ -146,6 +147,38 @@ public class Ab98ApiUtil {
} }
} }
/**
* 获取登录用户信息
* @param appname
* @param appsecret
* @param token
* @param companyName
* @return
*/
public static SsoLoginUserinfo getLoginUserInfoRemote(String appname,
String appsecret,
String token,
String companyName) {
String sign = DigestUtils.md5Hex(String.format("app=%s&token=%s%s", appname, token, appsecret));
String url = String.format("https://www.ab98.cn/api/getLoginUserInfo?token=%s&app=%s&company_name=%s&sign=%s"
,token
,appname
,""
,sign);
String res = HttpUtil.get(url);
try {
JSONObject o = JSONUtil.parseObj(res);
String state = o.getStr("state");
if (StringUtils.equalsIgnoreCase(state, "ok")) {
return o.get("data", SsoLoginUserinfo.class);
}
} catch (Exception e) {
log.error("获取登录用户信息失败", e);
}
return null;
}
// 基础响应对象 // 基础响应对象
@Data @Data
public static class BaseResponse { public static class BaseResponse {

View File

@ -0,0 +1,70 @@
package com.agileboot.domain.ab98.api;
import lombok.Data;
@Data
public class SsoLoginUserinfo {
private Long id;
/**
* 缓存失效时间戳()
*/
private Integer expire;
/**
* 账号状态状态1-正常 2-冻结
*/
private Integer status;
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 手机号码
*/
private String phone;
/**
* 微信昵称
*/
private String nickname;
/**
* 微信openid
*/
private String openId;
/**
* 微信unionId
*/
private String unionId;
/**
* 微信头像
*/
private String headImage;
/**
* 人脸图像
*/
private String face;
/**
* 身份证号码
*/
private String idCardNo;
/**
* 身份证正面
*/
private String idCardFront;
/**
* 身份证背面
*/
private String idCardBack;
/**
* 身份证地址
*/
private String address;
/**
* 原登录账号
*/
private String oldAccount;
}