微信登录原理

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。

  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。

  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。

  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。

  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。

  6. 小程序端,收到自定义登录态,存储storage。

  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。

  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。

  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

代码演示:

建一个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);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值