接口文档:
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
流程如下:
第三方应用获取access_token令牌后即可请求微信获取用户的信息,成功获取到用户的信息表示用户在第三方应用认证成功。
(1)首先修改前段代码产生微信二维码,并配置扫码后跳转到哪里,具体流程见第五章文档及配置微信文档
(2)使用微信进行扫码后跳转到指定的路径处理函数
@Controller public class WxLoginController { @Autowired WxAuthService wxAuthService; @RequestMapping("/wxLogin") public String wxLogin(String code, String state) throws IOException { log.debug("微信扫码回调,code:{},state:{}",code,state); //请求微信申请令牌,拿到令牌查询用户信息,将用户信息写入本项目数据库 XcUser xcUser = wxAuthService.wxAuth(code); if(xcUser==null){ return "redirect:http://www.51xuecheng.cn/error.html"; } String username = xcUser.getUsername(); return "redirect:http://www.51xuecheng.cn/sign.html?username="+username+"&authType=wx"; } }
调用wxAuthService.wxAuth(code)处理业务
@Override public XcUser wxAuth(String code) { //收到code调用微信接口申请access_token Mapaccess_token_map = getAccess_token(code); if (access_token_map == null) { return null; } //获取用户信息 System.out.println(access_token_map); String openid = access_token_map.get("openid"); String access_token = access_token_map.get("access_token"); //拿access_token查询用户信息 Map userinfo = getUserinfo(access_token, openid); if (userinfo == null) { return null; } //添加用户到数据库 非事务方法调用事务方法使用代理对象 XcUser xcUser = currentProxy.addWxUser(userinfo); return xcUser; } /** * 申请访问令牌,响应示例 { "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE", "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" } */ private Map getAccess_token (String code){ String wxUrl_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"; //请求微信地址 String wxUrl = String.format(wxUrl_template, appid, secret, code); log.info("调用微信接口申请access_token, url:{}", wxUrl); ResponseEntity exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class); String result = exchange.getBody(); log.info("调用微信接口申请access_token: 返回值:{}", result); Map resultMap = JSON.parseObject(result, Map.class); return resultMap; } /**获取用户信息,示例如下: { "openid":"OPENID", "nickname":"NICKNAME", "sex":1, "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", "privilege":[ "PRIVILEGE1", "PRIVILEGE2" ], "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" } */ private Map getUserinfo (String access_token, String openid){ String wxUrl_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s"; //请求微信地址 String wxUrl = String.format(wxUrl_template, access_token, openid); log.info("调用微信接口申请access_token, url:{}", wxUrl); ResponseEntity exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class); //防止乱码进行转码 也就说我们微信返回给我们的是ISO_8859_1 编码 我们使用这个编码读 然后在转码为UTF_8 String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); log.info("调用微信接口申请access_token: 返回值:{}", result); Map resultMap = JSON.parseObject(result, Map.class); return resultMap; } @Transactional public XcUser addWxUser(Map userInfo_map){ String unionid = userInfo_map.get("unionid").toString(); //根据unionid查询数据库 XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper ().eq(XcUser::getWxUnionid, unionid)); if(xcUser!=null){ return xcUser; } String userId = UUID.randomUUID().toString(); xcUser = new XcUser(); xcUser.setId(userId); xcUser.setWxUnionid(unionid); //记录从微信得到的昵称 xcUser.setNickname(userInfo_map.get("nickname").toString()); xcUser.setUserpic(userInfo_map.get("headimgurl").toString()); xcUser.setName(userInfo_map.get("nickname").toString()); xcUser.setUsername(unionid); xcUser.setPassword(unionid); xcUser.setUtype("101001");//学生类型 xcUser.setStatus("1");//用户状态 xcUser.setCreateTime(LocalDateTime.now()); xcUserMapper.insert(xcUser); XcUserRole xcUserRole = new XcUserRole(); xcUserRole.setId(UUID.randomUUID().toString()); xcUserRole.setUserId(userId); xcUserRole.setRoleId("17");//学生角色 xcUserRoleMapper.insert(xcUserRole); return xcUser; }
然后直接重定向到 return "redirect:http://www.51xuecheng.cn/sign.html?username="+username+"&authType=wx"; 就会自动登录(应该是前段配置的) springscruty就会到这
@Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { // 采用统一认证之后我们的s就是一个authparamsDto的json字符串 AuthParamsDto authParamsDto = null; try { //将认证参数转为AuthParamsDto类型 authParamsDto = JSON.parseObject(s, AuthParamsDto.class); } catch (Exception e) { log.info("认证请求不符合项目要求:{}",s); throw new RuntimeException("认证请求数据格式不对"); } // 我们不在使用 mybatis提供的框架了我们专门定义了一个处理认证请求的接口处理不同请求 //String userName = authParamsDto.getUsername(); //XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper().eq(XcUser::getUsername, userName)); //认证方法 String authType = authParamsDto.getAuthType(); AuthService authService = applicationContext.getBean(authType + "_authservice", AuthService.class); XcUserExt user = authService.execute(authParamsDto); /* //取出数据库存储的正确密码 String password =user.getPassword(); //用户权限,如果不加报Cannot pass a null GrantedAuthority collection String[] authorities= {"p1"}; //为了安全令牌中不妨密码 user.setPassword(null); //将user对象装换成json String userString = JSON.toJSONString(user); //创建UserDetails对象,权限信息待实现授权功能时再向UserDetail中加入 我们只需要将数据库中查询的密码封装进去,springscruty会自动帮我们进行校验密码是否正确 UserDetails userDetails = User.withUsername(userString).password(password).authorities(authorities).build();*/ return getUserPrincipal(user); } public UserDetails getUserPrincipal(XcUserExt user){ //用户权限,如果不加报Cannot pass a null GrantedAuthority collection String[] authorities = {"p1"}; String password = user.getPassword(); //为了安全在令牌中不放密码 user.setPassword(null); //将user对象转json String userString = JSON.toJSONString(user); //创建UserDetails对象 UserDetails userDetails = User.withUsername(userString).password(password ).authorities(authorities).build(); return userDetails; }
之后就会调用 authService.execute(authParamsDto); 当然是微信的impl
@Override public XcUserExt execute(AuthParamsDto authParamsDto) { //账号 String username = authParamsDto.getUsername(); XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper().eq(XcUser::getUsername, username)); if (user == null) { //返回空表示用户不存在 throw new RuntimeException("账号不存在"); } XcUserExt xcUserExt = new XcUserExt(); BeanUtils.copyProperties(user, xcUserExt); return xcUserExt; }
最后就会保存令牌到上下文结束。