微信小程序登录后端

一、 概念

code

code是用户登录凭证,个人理解为用户的授权码(需要用户本人授权给小程序,小程序才有权力获取到你这个用户的数据),code需要由小程序向微信服务器获取。

注意: 每个code只能使用一次,且有效期为5分钟。因此,在使用code进行登录时,需要及时将其转换成用户的openid和session_key等信息,以免出现code过期的情况

openid

openid是用来唯一标识用户的一个字符串。在微信小程序中,每个用户的openid都是唯一的。通过openid,小程序可以获取用户的基本信息,如头像、昵称等。
大概就是,不同的用户,在不同的小程序上会有不同的openid,后台可以通过openid+session_key来对使用本小程序的用户进行标识(某个openid就是某个用户)

注意: 同一个用户在不同的小程序中拥有不同的openid。因此,在开发小程序时,不能使用openid来进行用户的唯一性判断。

二、 流程

流程图

微信小程序登录后端_第1张图片
步骤分析:

  1. 小程序端,调用wx.login()获取code授权码。调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
  2. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
  3. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识
  4. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验
  5. 小程序端,收到自定义登录态(token),存储storage。
  6. 小程序端,后绪通过wx.request()发起业务请求时,携带token。
  7. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
  8. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

三、 服务端代码

配置文件

yml

项目:
	wechat:
	    appid: ${sky.wechat.appid}
	    secret: ${sky.wechat.secret}
#**配置为微信用户生成jwt令牌时使用的配置项:**
 	user-secret-key: itheima
    user-ttl: 7200000
    user-token-name: authentication

DTO传code

@Data
public class UserLoginDTO implements Serializable {

    private String code;

}

VO类

返回用户的id , openid,token给前端;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {

    private Long id;
    private String openid;
    private String token;

}

逻辑梳理

controller层

  1. 接收参数,获取code,校验数据;
  2. 给用户生成jwt令牌;

service层

  1. 通过code,向微信服务器发送请求获取openid
  2. 判断获取不获取得到(openid == null)
    2.1 为空 抛出业务异常(code可能是错误的);
  3. 判断是否为新用户(openid是否在数据库中已经存在)
    不存在 : 自动注册
    存在: 直接返回;

Controller

public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private JwtProperties jwtProperties;

    @PostMapping("/login")
    @ApiOperation("微信登录")
    public Result<UserLoginVO> login(@PathVariable UserLoginDTO userLoginDTO){
        log.info("微信用户登录:{}",userLoginDTO.getCode());
        User user = userService.wxLogin(userLoginDTO);

        //登录成功后 为微信用户生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID, user.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getUserSecretKey(),
                jwtProperties.getUserTtl(),
                claims);

        UserLoginVO userLoginVO = UserLoginVO.builder()
                .id(user.getId())
                .openid(user.getOpenid())
                .token(token)
                .build();
        return Result.success(userLoginVO);
    }

}

ServiceImpl

public class UserServiceImpl implements UserService {
    @Autowired
    private WeChatProperties weChatProperties;
    @Autowired
    private UserMapper userMapper;

    //微信服务接口地址
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    public User wxLogin(UserLoginDTO userLoginDTO) {

            String openid = getOpenid(userLoginDTO.getCode());

            //1、 判断openid是否为空,如果为空表示登录失败,抛出业务异常
            if (openid == null) {
                throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
            }

            //2、 判断当前用户是否为新用户
            User user = userMapper.getByOpenid(openid);

            //3、 如果是新用户,自动完成注册
            if (user == null) {
                user = User.builder()
                        .openid(openid)
                        .createTime(LocalDateTime.now())
                        .build();
                userMapper.insert(user);
            }

            //4、返回这个用户对象
            return user;
        }

    /**
         * 调用微信接口服务,获取微信用户的openid
         * @param code
         * @return
         */
        private String getOpenid (String code){
            //调用微信接口服务,获得当前微信用户的openid
            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, map);

            JSONObject jsonObject = JSON.parseObject(json);
            String openid = jsonObject.getString("openid");
            return openid;
        }

}

mapper 和 service接口就省略啦
mapper的insert记得键入主键的返回useGeneratedKeys=“true”

接着就是设置用户的 jwt令牌拦截器

@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler   目标方法
     * @return true ,代表放行, false,表示禁止放行
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌   因为token存放在请求头中,
        String token = request.getHeader(jwtProperties.getUserTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前用户id:", userId);
            BaseContext.setCurrentId(userId); //通过LocalThread,把id存到该线程的存储空间中;
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }

WebMvcConfiguration配置拦截器;

 protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenUserInterceptor)
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");
    }

参考链接:https://juejin.cn/post/7212074532340908091

你可能感兴趣的:(Project,微信小程序,spring,boot,java)