赞
踩
# outh2相关配置
wx:
open:
#微信开放平台 appid
app_id: xxxxxxxxxxxxxxx
# 微信开放平台 appsecret
app_secret: xxxxxxxxxxxxxxxxx
# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
redirect_url: http://xxxxxxxxxxx
@Component //@PropertySource("classpath:application.yml") public class ConstantPropertiesUtil implements InitializingBean { @Value("${wx.open.app_id}") private String appId; @Value("${wx.open.app_secret}") private String appSecret; @Value("${wx.open.redirect_url}") private String redirectUrl; public static String WX_OPEN_APP_ID; public static String WX_OPEN_APP_SECRET; public static String WX_OPEN_REDIRECT_URL; @Override public void afterPropertiesSet() throws Exception { WX_OPEN_APP_ID = appId; WX_OPEN_APP_SECRET = appSecret; WX_OPEN_REDIRECT_URL = redirectUrl; } }
@GetMapping("/ucenterService/login") @ApiOperation("生成微信扫描二维码") public String genQrConnect(HttpSession session) { LOGGER.info("生成微信扫描二维码 start "); /* 固定地址,后面拼接参数 String url = "https://open.weixin.qq.com/" + "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";*/ // 微信开放平台授权baseUrl %s相当于?代表占位符(此地址为固定地址,固定参数 ) String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" + "?appid=%s" + "&redirect_uri=%s" + "&response_type=code" + "&scope=snsapi_login" + "&state=%s" + "#wechat_redirect"; // 回调地址 // 获取业务服务器重定向地址(需要在微信开放平台配置) String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL; try { //对redirect_url进行URLEncoder编码 redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(20001, e.getMessage()); } // 防止csrf攻击(跨站请求伪造攻击) //String state = UUID.randomUUID().toString().replaceAll("-", "");//一般情况下会使用一个随机数 // 这里填写你在ngrok的前置域名 String state = "xxxx"; LOGGER.info("state = {}", state); // 采用redis等进行缓存state 使用sessionId为key 30分钟后过期,可配置 //键:"wechar-open-state-" + httpServletRequest.getSession().getId() //值:satte //过期时间:30分钟 // 生成qrcodeUrl String qrcodeUrl = String.format( baseUrl, ConstantWxUtils.WX_OPEN_APP_ID, redirectUrl, state ); LOGGER.info("生成微信扫描二维码 end 重定向地址为:{}", qrcodeUrl); return "redirect:" + qrcodeUrl; }
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识 |
redirect_uri | 是 | 请使用urlEncode对链接进行处理 |
response_type | 是 | 填code |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即 |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
请求登录的流程大致是这样子的:
- 首先请求一个固定的地址(该地址由微信官方提供,我们只需要按照要求提供相应的参数来调用即可)
- 为了安全性,redirect_url需要进行URLEncoder编码
- 请求完成后,微信会返回给我们一个地址,我们只需要重定向到该地址,就可以在该页面看到相应的二维码信息。
- 在我们扫码点击登录后,微信官方就会将我们的请求转发到我们的回调方法上(需要自己配置),我们在回调方法上来进行一些数据的操作。
@GetMapping("callback") @ApiOperation(value = "获取扫描人信息,添加数据", tags = "当用户扫描二维码登录后,会回调到本方法中") public String callback(String code, String state, HttpSession session) { LOGGER.info("用户微信扫描登陆之后开始进行回调: code = {},state = {}", code, state); // 从redis中将state获取出来,和当前传入的state作比较(正确的做法) // 如果一致则放行,如果不一致则抛出异常:非法访问(正确的做法) try { // 1 获取code值,临时票据,类似于验证码 // 2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid // 向认证服务器发送请求换取access_token String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" + "?appid=%s" + "&secret=%s" + "&code=%s" + "&grant_type=authorization_code"; // 拼接三个参数 :id 秘钥 和 code值 String accessTokenUrl = String.format(baseAccessTokenUrl, ConstantWxUtils.WX_OPEN_APP_ID, ConstantWxUtils.WX_OPEN_APP_SECRET, code); // 请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid // 使用httpclient发送请求,得到返回结果 String accessTokenInfo = HttpClientUtils.get(accessTokenUrl); LOGGER.info("accessTokenInfo = {}", accessTokenInfo); // 从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid // 把accessTokenInfo字符串转换map集合,根据map里面key获取对应值 // 使用json转换工具 Gson Gson gson = new Gson(); HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class); String accsess_token = (String) mapAccessToken.get("access_token"); String openid = (String) mapAccessToken.get("openid"); LOGGER.info("成功得到accsess_token和openid accsess_token = {},openid = ", accsess_token, openid); // 把扫描人信息添加数据库里面 // 判断数据表里面是否存在相同微信信息,根据openid判断(openid是唯一的) xXXXX XXX = xxxxService.getOpenId(openid); // 之前没有用微信登陆过 if (null == XXXX) { LOGGER.info("当前用户尚未使用过微信登录,将会该用户的信息存入数据库"); // 3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息 // 访问微信的资源服务器,获取用户信息 String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" + "?access_token=%s" + "&openid=%s"; //拼接两个参数 String userInfoUrl = String.format( baseUserInfoUrl, accsess_token, openid ); // 发送请求,得到微信官方返回的用户信息 String userInfo = HttpClientUtils.get(userInfoUrl); LOGGER.info("微信官方返回的用户信息为:{}", userInfo); //获取返回userinfo字符串扫描人信息 HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class); String nickname = (String) userInfoMap.get("nickname");//昵称 String headimgurl = (String) userInfoMap.get("headimgurl");//头像 //将用户的头像信息和昵称存入数据库 略 LOGGER.info("成功将微信信息存入数据库中,存入的信息为:{}", member); } LOGGER.info("当前用户已经使用过微信进行登录,直接返回该用户的信息"); // TODO 登录 // 生成jwt String token = JwtUtils.getJwtToken(xxx.getId(), xxx.getNickname()); // 存入cookie //CookieUtils.setCookie(request, response, "guli_jwt_token", token); // 因为端口号不同存在跨域问题,cookie不能跨域,所以这里使用url重写 LOGGER.info("微信登陆成功后重定向到首页,携带的token信息为:{}", token ); return "redirect:http://xxx:xxxx?token=" + token; } catch (Exception e) { LOGGER.error("微信登录失败,异常信息为:{}", e.toString()); throw new RunTimeException(20001, "登录失败"); } }
回调方法简介
- 首先我们可以直接在请求参数中添加code 和 state,这两个信息就类似于验证码,我们在通过这两个数据去请求微信固定的地址,得到两个值accsess_token 和 openid 微信返回的数据为JSON格式,我们可以通过fastJson或其他来将数据转换为Map集合,在通过map集合来取到其中的这两个数据。
- 然后在通过accsess_token 和 openid这两个数据请求另一个微信官方的地址来获取微信账号的信息(包括微信账号的头像地址和微信名称)
- 获取到这些信息之后,便可一通过openid(唯一标识)进行判断,来判断用户之前是否使用过微信登录,如果没有登陆过,就可以将用户信息保存到数据库中。
- 使用JWT公具类来根据用户的openid和微信昵称来生成token
- 微信登陆成功后重定向到到目标页面,并且携带token信息
- 前端页面从请求路径中达到token信息,将token存储在cookie或者请头头中。
- 用户携带token请求用户信息,后端解析通过,将用户信息返回给前端,前端将用户信息作展示。
export default { data() { return { token: '', } }, created() { this.token = this.$route.query.token if (this.token) { this.wxLogin() } }, methods: { wxLogin() { if (this.token == '') return //把token存在cookie中、也可以放在localStorage中 cookie.set('guli_token', this.token, {domain: 'localhost'}) cookie.set('guli_ucenter', '', {domain: 'localhost'}) //登录成功根据token获取用户信息 userApi.getLoginInfo().then(response => { this.loginInfo = response.data.data.item //将用户信息记录cookie cookie.set('guli_ucenter', this.loginInfo, {domain: 'localhost'}) }) } } }
其中涉及到的jwt工具类和HttpClientUtils可以私聊我,我私发给你
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。