全部开发者教程

企业级在线办公系统

首页 慕课教程 企业级在线办公系统 企业级在线办公系统 15-1 微信扫码登陆(后端生成二维码图片)

emos网站除了可以使用帐户密码登陆之外,还拥有微信扫码登陆功能。类似于网页版的微信,我们在手机微信上面扫描二维码就能登陆Web微信一样。我们要开发的就是这种相似的功能。

图片描述

这节课我们先来把生成二维码图片的代码给实现了。这个二维码图片是后端emos-api项目的Web方法生成的,里面包含的信息是“login@@uuid字符串”,其中“@@”是字符串中的分隔字符。“login代表的是命令名称”,这一章我们会给小程序添加扫描二维码签到会议的功能。所以小程序扫描出来的二维码内容中理应包含命令名称,这样小程序才知道扫码之后到底应该登陆还是签到。“uuid字符串”是redis中缓存的一个字符串,为什么这样设计,一会儿会说到。

一、生成二维码图片

图片描述

UserService.java接口中定义生成二维码的抽象方法。

public interface UserService {
    ……
    public HashMap createQrCode();
}

UserServiceImpl.java类中实现抽象方法,生成二维码图片。

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private RedisTemplate redisTemplate;
    ……
    
    @Override
    public HashMap createQrCode() {
        String uuid = IdUtil.simpleUUID();
        //key是uuid,value的false代表该uuid没有被使用,防止该二维码被重复扫码
        redisTemplate.opsForValue().set(uuid, false, 5, TimeUnit.MINUTES);
        //设置二维码信息
        QrConfig config = new QrConfig();
        config.setHeight(160);
        config.setWidth(160);
        config.setMargin(1);
        //把生成的二维码图片转换成base64字符串
        String base64 = QrCodeUtil.generateAsBase64("login@@@" + uuid, config, ImgUtil.IMAGE_TYPE_JPG);
        HashMap map = new HashMap() {{
            put("uuid", uuid);
            put("pic", base64);
        }};
        return map;
    }
}

UserController.java类中定义Web方法,并且用Swagger测试该Web方法。

@RestController
@RequestMapping("/user")
@Tag(name = "UserController", description = "用户Web接口")
public class UserController {
    /**
     * 生成登陆二维码的字符串
     */
    @GetMapping("/createQrCode")
    @Operation(summary = "生成二维码Base64格式的字符串")
    public R createQrCode() {
        HashMap map = userService.createQrCode();
        return R.ok(map);
    }
}

二、扫码登陆

图片描述
用户在微信上面扫描了二维码之后,会调用后端Web方法,修改Redis里面缓存UUID对应的VALUE值,如果false变成了userId,说明用户已经在微信上扫码成功了。我们要接收微信小程序提交过来的Ajax请求,里面就包括了用户的临时授权code字符串,经过转换之后我们拿到openId字符串。因为每个用户的openId字符串都是唯一的,所以我们可以根据openId字符串作为查询条件,查询userId。如果存在userId,就可以判定该用户登陆,我们把userId写入Redis。然后浏览器端不停地轮询Redis,如果发现UUID对应的Value已经变成了userId,这时候就认为用户登陆成功,颁发Token令牌即可。

TbUserDao.xml文件中定义SQL语句,根据OpenId查询用户的UserId。

<select id="searchIdByOpenId" parameterType="String" resultType="Integer">
    SELECT id FROM tb_user WHERE open_id=#{openId} AND status = 1
</select>

TbUserDao.java接口中声明抽象DAO方法。

@Mapper
public interface TbUserDao {
    ……
    public Integer searchIdByOpenId(String openId);
}

UserService.java接口中定义抽象方法。

public interface UserService {
    ……
    public boolean checkQrCode(String code, String uuid);
}

UserServiceImpl.java类中实现抽象方法。

@Service
public class UserServiceImpl implements UserService {
    @Value("${wx.app-id}")
    private String appId;

    @Value("${wx.app-secret}")
    private String appSecret;
    
    ……
    @Override
    public boolean checkQrCode(String code, String uuid) {
        boolean bool = redisTemplate.hasKey(uuid);
        if (bool) {
            String openId = getOpenId(code);
            Integer userId = userDao.searchIdByOpenId(openId);
            redisTemplate.opsForValue().set(uuid, userId);
            if (userId != null && userId > 0) {
                return true;
            }
        }
        return false;
    }
    
    //用于把Code临时授权转换成OpenID
    private String getOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("js_code", code);
        map.put("grant_type", "authorization_code");
        String response = HttpUtil.post(url, map);
        JSONObject json = JSONUtil.parseObj(response);
        String openId = json.getStr("openid");
        if (openId == null || openId.length() == 0) {
            throw new RuntimeException("临时登陆凭证错误");
        }
        return openId;
    }
}

创建CheckQrCodeForm.java类,封装微信小程序Ajax提交的请求。

@Data
@Schema(description = "检验登陆验证码表单")
public class CheckQrCodeForm {
    @NotBlank(message = "uuid不能为空")
    @Schema(description = "uuid")
    private String uuid;

    @NotBlank(message = "临时授权不能为空")
    @Schema(description = "临时授权")
    private String code;

}

UserController.java类中,定义Web方法。该方法需要小程序调用,所以不要再Swagger上测试。

@RestController
@RequestMapping("/user")
@Tag(name = "UserController", description = "用户Web接口")
public class UserController {
    ……
    @PostMapping("/checkQrCode")
    @Operation(summary = "检测登陆验证码")
    public R checkQrCode(@Valid @RequestBody CheckQrCodeForm form) {
        boolean bool = userService.checkQrCode(form.getCode(), form.getUuid());
        return R.ok().put("result", bool);
    }
}