步骤分析:
-
小程序端,调用wx.login()获取code,就是授权码。
-
小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
-
开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
-
开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
-
开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
-
小程序端,收到自定义登录态,存储storage。
-
小程序端,后绪通过wx.request()发起业务请求时,携带token。
-
开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
-
开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。
代码演示:
建一个Dto用于接受小程序传过来的code。
@Data
public class UserLoginDTO implements Serializable {
private String code;
}
在开发者创建一个登录端的接口
@RestController
@RequestMapping("/user/user")
@Slf4j
@Api(tags = "C端用户相关接口")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
@ApiOperation("用户登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.warn("{}", userLoginDTO);
return userService.login(userLoginDTO);
}
}
返回值UserLoginVO用于返回最后的openid和token
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {
private Long id;
private String openid;
private String token;
}
service层
public interface UserService {
/**
* 用户微信登录
* @param userLoginDTO
* @return
*/
Result<UserLoginVO> login(UserLoginDTO userLoginDTO);
}
service层实现类
@Service
@Transactional
@Slf4j
public class UserServiceImpl implements UserService {
public static final String WX_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private JwtProperties jwtProperties;
@Autowired
private UserMapper userMapper;
@Override
public Result<UserLoginVO> login(UserLoginDTO userLoginDTO) {
// 一.校验参数
if(Objects.isNull(userLoginDTO) || Objects.isNull(userLoginDTO.getCode())) {
throw new ArgsErrorException(MessageConstant.ARGS_ERROR);
}
// 二.处理业务
// 1.请求微信登录服务器,获取openid
String openid = getOpenid(userLoginDTO.getCode());
// 2.判断是否有openid
if(StringUtils.isBlank(openid)) {
// 3.无,直接响应【非法用户】
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
// 4.有,根据openid查询用户表获取用户信息
User user = userMapper.findByOpenid(openid);
if(Objects.isNull(user)) {
// 5.没有用户信息,增加一个用户
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
//主键回填
userMapper.insert(user);
}
// 三.封装数据
//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());
String token = JwtUtil.createJWT(
jwtProperties.getUserSecretKey(),
jwtProperties.getUserTtl(),
claims);
// 有用户信息,根据用户信息生成token,返回给前端
UserLoginVO vo = new UserLoginVO();
vo.setId(user.getId());
vo.setOpenid(user.getOpenid());
vo.setToken(token);
return Result.success(vo);
}
/**
* 获取openid
* @param code
* @return
*/
private String getOpenid(String code) {
Map<String, String> map = new HashMap<>();
map.put("appid", weChatProperties.getAppid());
map.put("secret", weChatProperties.getSecret());
map.put("js_code", code);
map.put("grant_type", "authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN_URL, map);
JSONObject jsonObject = JSON.parseObject(json);
return jsonObject.getString("openid");
}
}
我们主要来分析下获取openid的这个代码:
/**
* 获取openid
* @param code
* @return
*/
private String getOpenid(String code) {
Map<String, String> map = new HashMap<>();
map.put("appid", weChatProperties.getAppid());
map.put("secret", weChatProperties.getSecret());
map.put("js_code", code);
map.put("grant_type", "authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN_URL, map);
JSONObject jsonObject = JSON.parseObject(json);
return jsonObject.getString("openid");
}
如何通过接受前端传来的code获取openid?
第一官方给个openid首先它是一个字符串,而获得openid的关键你需要下面四个参数。
appid和secret你可以通过配置文件获得:
wechat:
appid: wxf663a962bea8a958
secret: a3039f37ee7301e3749a812557515190
通过WeChatProperties来获取配置文件
@Component
@ConfigurationProperties(prefix = "sky.wechat")
@Data
public class WeChatProperties {
private String appid; //小程序的appid
private String secret; //小程序的秘钥
private String mchid; //商户号
private String mchSerialNo; //商户API证书的证书序列号
private String privateKeyFilePath; //商户私钥文件
private String apiV3Key; //证书解密的密钥
private String weChatPayCertFilePath; //平台证书
private String notifyUrl; //支付成功的回调地址
private String refundNotifyUrl; //退款成功的回调地址
}
通过HttpClientUtil的doGet方法发送Get方式请求,获得openid的json字符串。
String json = HttpClientUtil.doGet(WX_LOGIN_URL, map);
WX_LOGIN_URL是(https://api.weixin.qq.com/sns/jscode2session)是微信登录功能的API地址。当用户使用微信小程序或网页登录时,开发者需要将用户的登录凭证(js_code)发送到这个URL,以获取用户唯一标识(openid)和会话密钥(session_key)。
map的键分别对应appid和secret还有js_code和grant_type还有对应的value值,
appid和secret可以读配置文件获得(这两个要写你自己的),js_code是前端传的code,grant_type的值是死的直接填入authorization_code。
Map<String, String> map = new HashMap<>();
map.put("appid", weChatProperties.getAppid());
map.put("secret", weChatProperties.getSecret());
map.put("js_code", code);
map.put("grant_type", "authorization_code");
获得openid的json字符串,然后使用JSON.parseObject()把json解析成JSONObject对象,然后返回jsonObject.getString("openid")(jsonObject本质就是一个map)。
JSONObject jsonObject = JSON.parseObject(json);
return jsonObject.getString("openid");
接下来你可以判断有没有openid了。
// 2.判断是否有openid
if(StringUtils.isBlank(openid)) {
// 3.无,直接响应【非法用户】
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
有,根据openid查询用户表获取用户信息,没有用户信息就在表里新增一个用户信息。
// 4.有,根据openid查询用户表获取用户信息
User user = userMapper.findByOpenid(openid);
if(Objects.isNull(user)) {
// 5.没有用户信息,增加一个用户
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
//主键回填
userMapper.insert(user);
}
yml配置jwt
sky:
jwt:
# 设置jwt签名加密时使用的秘钥
admin-secret-key: itcast
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
#用户端的jwt信息
user-secret-key: itheima
user-ttl: 7200000
user-token-name: authentication
JwtProperties:秘钥和过期时间是通过配置文件拿到的
@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {
/**
* 管理端员工生成jwt令牌相关配置
*/
private String adminSecretKey;
private long adminTtl;
private String adminTokenName;
/**
* 用户端微信用户生成jwt令牌相关配置
*/
private String userSecretKey;//解析token秘钥
private long userTtl;//有效期
private String userTokenName;
}
生成token值
// 三.封装数据
//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());
String token = JwtUtil.createJWT(
jwtProperties.getUserSecretKey(),
jwtProperties.getUserTtl(),
claims);
// 有用户信息,根据用户信息生成token,返回给前端
jwtProperties.getUserSecretKey()`:这是用户的密钥,用于签名生成的令牌。
jwtProperties.getUserTtl()`:这是令牌的过期时间,它是一个长整型表示的时间戳。
claims:这是之前创建的存储声明信息的
Map` 对象。
调用 JwtUtil.createJWT
方法后,将返回生成的 JWT 令牌,存储在 token
变量中。
最后把token和openid封装到UserLoginVO中去,然后返回。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {private Long id;
private String openid;
private String token;}
UserLoginVO vo = new UserLoginVO();
vo.setId(user.getId());
vo.setOpenid(user.getOpenid());
vo.setToken(token);
return Result.success(vo);