一起学习网站开发之基于Spring boot的微信登录开发流程和知识点

前言

前一段时间刚入门了springboot2.0x基础,但在学完后感觉很空,学得很泛,有种蜻蜓点水的感觉(可能是我太菜了,哈哈),于是就想找个项目练练手,看看自己的水平能做些什么。。。。话不多说,接下来我将从我的角度介绍微信扫一扫登录的开发流程和知识点,但仅仅是开发流程和知识点,因为本人并没有开发出最终的应用场景,原因很悲凉,就是在前期准备上,我无能为力!

开发前期准备

官方文档原话:

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。 在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。

总结:要想开发微信OAuth2.0授权登录

1、在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用。

2、获得相应的AppID和AppSecret,申请微信登录且通过审核后。

​ 具体请参照

可大多数入手该项目的新手都会倒在第一步,就是微信开放平台注册开发者帐号,因为注册开发者帐号需要企业资料,但一般新手何来有企业资料,因此这点对新手是非常不友好的,但这不应该成为阻碍我们继续学习的动力对吧,所以我们就当我们拥有了相应的AppID和AppSecret,然后先知道是怎么用的。

做大家都熟悉的事情

使用微信扫码登录,我相信这是大家都做过的事情。下面我将用一张图结合我们平时”表面“的操作来和大家一起了解微信OAuth2.0实际的交互流程。

应用情景:一次,我女朋友诸葛大力无意点进豆瓣网站,然后看上了一把书,想要购买,但要先登录,于是就有了图1的操作:

经过上图的简单操作,我女朋友诸葛大力就完成登录了,但第三方平台又是怎么确认诸葛大力的用户身份的(如何拿到她的用户信息),从上图无从知晓,那我们就接着看下图2:

哇,通过看完图2,你是不是已经。。。。。。对,你还是懵逼(哈哈),回归正题。

1、拉起第三方应用,或重定向第三方应用,这个场景你一定不陌生,就是你扫码登录后页面自动跳转回第三方平台的页面,至于是网站首页还是其他页面,根据业务的实际需求实现就好,具体如何实现,不急,后面会讲,现在先了解概念。

2、什么code?code是授权凭证,如 豆瓣(A) -> 微信开发平台(B) 发起授权,想获取授权⽤户信息,那A必须携带授权码,才可以向B获取授权信息。

3、什么access_token? 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。也就是我们平时登录后所显示的头像、微信号等信息,要通过access_token来进行接口调用才能得到。

以上,整体概念我就都抛出来了,后面的实战也会相当的精彩,以上内容如有错误或不当描述,欢迎讨论指正!

实操整体流程:

网站应用

主要分为四大步

​ 1、请求获取Code

​ 2、用户同意授权与否(同意就带code返回,不同意就不带)

​ 3、获取access_token

​ 4、通过access_token调用接口获取用户个人信息(UnionID机制)

精彩的实战

首先,我们要先配置好AppID和Appsecret,重定向的url,微信开放平台⼆维码连接 。

1、配置好AppID和Appsecret是因为要用它们换取access_token,不然第三方平台怎么拿的到用户的基本个人信息呢。

2、配置好重定向的url,是为登录成功后微信平台能主动跳转到我们后台所写好的接口,方便我们拿到code。(这个如果你没接触过的话可能有点懵,没关系,自信且勇敢地看下去,下面我会尽力助你明白)

3、配置好微信开放平台⼆维码连接url,是因为要通过这个url去获取登录的二维码,且这个url含有AppID。

话不多说,看代码
  1. 配置好AppID和Appsecret

application.properties

#微信开放平台配置
wxopen.appid=wx025575eac69a2d5b
wxopen.appsecret=deeae310a387fa9d3e8f3830ce64caac
#重定向url
wxopen.redirect_url=http://api/v1/wechat/user/callback

说明:这里的重定向url是随便写的,仅是演示,实际开发再确认实际的url。

●为了方便取值,我们可以增加一个配置类,这里命名为WebChatConfig

WebChatConfig

/*** 微信配置类*/
@Configuration
//与application.properties文件映射
@PropertySource(value = "classpath:application.properties")
public class WeChatConfig {/*** 开发平台appid*/@Value("${wxopen.appid}")//等同于取application.properties中wxopen.appid的值,$是取值的意思private String openAppid;/*** 开放平台appsecret*/@Value("${wxopen.appsecret}")private String openAppsecret;/*** 开放平台回调url*/@Value("${wxopen.redirect_url}")private String openRedirectUrl;/*** 微信开放平台二维码连接*/private final static String OPEN_QRCODE_URL="https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect";public String getOpenAppid() {return openAppid;}public void setOpenAppid(String openAppid) {this.openAppid = openAppid;}public String getOpenAppsecret() {return openAppsecret;}public void setOpenAppsecret(String openAppsecret) {this.openAppsecret = openAppsecret;}public String getOpenRedirectUrl() {return openRedirectUrl;}public void setOpenRedirectUrl(String openRedirectUrl) {this.openRedirectUrl = openRedirectUrl;}}

● 通过增加配置类WebChatConfig,之后在开发过程中要使用appid和appsecret时,我们只需要调用相应的get方法就可以实现了,相当于做了一层简单的封装。

● 还有你发现,我已经配置好了微信开放平台二维码连接的url,先来看看官方文档

第三方使用网站应用授权登录前请注意已获取相应网页授权作用域(scope=snsapi_login),则可以通过在PC端打开以下链接: https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与审核时填写的授权域名不一致或scope不为snsapi_login。

其他的先不用看(都是小case),我们关注重点:可以通过在PC端打开以下链接: https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect ,意思就是我们要向微信开发平台发出以上这个链接请求,就可以获得微信开放平台二维码(用户就是通过扫描该二维码登录)。

我们来观察这个链接包含了那些参数:

参数                    是否必须                      说明
appid                    是                      应用唯一标识redirect_uri           是               请使用urlEncode对链接进行处理response_type            是                        填codescope                     是             应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前                                                仅填写snsapi_login即state                   否           用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可                                       用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,                                       可设置为简单的随机数加session进行校验

显然,我们是要以链接: https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 为模板,然后把APPID、REDIRECT_URl、STATE#wechat_redirect换成我们自己独有的数据参数(其他两个为默认参数),再发送链接请求,即可获得登录二维码, 一切是不是已经豁然开朗了?不,你可能会问,那redirect_uri和state到底是什么?它们如何来?我们又要怎么使用?继续看下去

柯南推理

柯南:我已经知道什么是redirect_uri了,请看图:

注意:要使用urlEncode对redirect_uri进行处理。

再说说什么是state,其实state就是一个回调的页面链接。

场景演示一(未登录):

场景演示二(登录成功后):

●综上你会发现,登录成功后网页会自动跳转到第三方网站首页,这是怎么做的呢,这就是state的作用,state的参数就是登录成功后回调的网页链接。

说了这么多,相信你也迫不及待看代码(你心想:这人废话真多)

定义一个JsonData类:为的是规范前、后端协议,告诉前端请求的状态、数据、描述
public class JsonData implements Serializable {/****/private static final long serialVersionUID = 1L;private Integer code; // 状态码 0 表示成功,1表示处理中,-1表示失败private Object data; // 数据private String msg;// 描述public JsonData() {}public JsonData(Integer code, Object data, String msg) {this.code = code;this.data = data;this.msg = msg;}// 成功,传入数据public static JsonData buildSuccess() {return new JsonData(0, null, null);}// 成功,传入数据public static JsonData buildSuccess(Object data) {return new JsonData(0, data, null);}// 失败,传入描述信息public static JsonData buildError(String msg) {return new JsonData(-1, null, msg);}// 失败,传入描述信息,状态码public static JsonData buildError(String msg, Integer code) {return new JsonData(code, null, msg);}// 成功,传入数据,及描述信息public static JsonData buildSuccess(Object data, String msg) {return new JsonData(0, data, msg);}// 成功,传入数据,及状态码public static JsonData buildSuccess(Object data, int code) {return new JsonData(code, data, null);}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}@Overridepublic String toString() {return "JsonData [code=" + code + ", data=" + data + ", msg=" + msg+ "]";}}

开发WeChatController

1、拼装微信扫一扫登录url

2、微信扫码登录成功后回调

/***   WechatController之拼装微信扫一扫登录url*/@Controller
@RequestMapping("/api/v1/wechat")
public class WechatController {@Autowiredprivate WeChatConfig weChatConfig; //注入WeChatConfig@Autowiredprivate UserService userService;//有关微信回调的代码,不用管!后面会讲。/*** 拼装微信扫一扫登录url* @return*/@GetMapping("login_url")@ResponseBody//需要传入state参数,根据实际请求获取public JsonData loginUrl(@RequestParam(value = "access_page",required = true)String accessPage) throws UnsupportedEncodingException {String redirectUrl = weChatConfig.getOpenRedirectUrl(); //获取开放平台重定向地址String callbackUrl = URLEncoder.encode(redirectUrl,"GBK"); //编码处理为官方要求String qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(),weChatConfig.getOpenAppid(),callbackUrl,accessPage); // 格式化weChatConfig.getOpenQrcodeUrl(),把原模板链接的APPID、REDIRECT_URl、STATE替换成我们的。return JsonData.buildSuccess(qrcodeUrl);}/*** 微信扫码登录回调地址*///为了方便讲解,先忽略微信回调部分的代码//...................................
}

启动application,测试一把,发送一下请求

默认端口:8080,state参数我随便填了一个www.baidu.com,但实际场景中的state参数是由前端获取传入后端的,简单来说是由前端决定的,这里我们不展开讨论。

再来看看请求的结果:

可以看到,返回的是我们自定义的JsonData格式,我把data拉出来看看

"https://open.weixin.qq.com/connect/qrconnect?appid=wx025575eac69a2d5b&redirect_uri=http%3A%2F%2Fsunny.viphk.ngrok.org%2Fapi%2Fv1%2Fwechat%2Fuser%2Fcallback&response_type=code&scope=snsapi_login&state=www.baidu.com#wechat_redirect"

可以明显的看到url中有我们自己的appid=wx025575eac69a2d5b,自定义的、经编码处理的redirect_uri=http%3A%2F%2Fsunny.viphk.ngrok.org%2Fapi%2Fv1%2Fwechat%2Fuser%2Fcallback,和回调页面链接state=www.baidu.com#wechat_redirect,到这步,我们拼装好的微信扫一扫登录url了,那你可能会问:那微信二维码呢,有个url有什么用?

而其实这时我们再去访问url链接,奇迹就发生了!

​ (好吧,这是理想情况。。。)

像我这样没有appid等微信认证授权的,结果如下:

太难了。。。

此处有苦说不出。。。

回归正题

问:假设现在是已经显示出微信二维码,那此时如果我女朋友诸葛大力她打开微信进行扫码,她能登陆成功吗?

显然是不能嘛对吧!

为什么?我们还没完成回调的开发呢!微信那边code就无法回调给我们,那获取access_token就更不用说了。

那就继续第二步:微信扫码登录成功后回调

@Controller
@RequestMapping("/api/v1/wechat")
public class WechatController {@Autowiredprivate WeChatConfig weChatConfig; //注入WeChatConfig@Autowiredprivate UserService userService;//有关微信回调的代码,不用管!后面会讲。/*** 拼装微信扫一扫登录url* @return*///为了方便讲解,忽略微信登录部分的代码//.............../*** 微信扫码登录,回调地址* @param code* @param state* @param response* @throws IOException*/@GetMapping("/user/callback") //回调接口public void wechatUserCallback(@RequestParam(value = "code",required = true) String code,String state, HttpServletResponse response) throws IOException {//保存用户信息User user = userService.saveWeChatUser(code);if(user != null){//通过jwt生成token返回给前端String token = JwtUtils.geneJsonWebToken(user);// state 当前用户的页面地址,需要拼接 http://  这样才不会站内跳转response.sendRedirect(state+"?token="+token+"&head_img="+user.getHeadImg()+"&name="+URLEncoder.encode(user.getName(),"UTF-8"));}}

我知道:你可能懵逼了!让我来猜一猜你疑惑的点:

1、回调接口为什么是**@GetMapping("/user/callback")**

2、哪冒出来一个User和userService

3、jwt又是什么?为什么要用jwt

4、state的为什么是这样拼接?意图何在?

解决上面4个问题,基本就收工啦!

插一句:你可能会想:我这人,怎么User、userService和jwt这些知识点都没提前讲,就突然拿出来给我看,都不考虑我的感受的吗,这也太不友好了吧。。。其实不是的,因为我是完全按照我第一次开发的思路来讲解的,现实开发其实也是如此,不可能说一开始就什么都准备好的,很多东西是开发过程中发现需要用到才逐渐加上的,当然,如果你都是老司机了,开发流程、经验都杠杠的,那么像User、userService和jwt等等这些,你可能一开始就会准备好对吧。所以说,看不懂不要慌,很正常,继续看嘛。

不急,我们慢慢看,要自信!

先来看第一个问题:回调接口为什么是**@GetMapping("/user/callback")**

首先,@GetMapping("/api/v1/wechat")不是一个完整的接口,你可能忘了WechatController一开始就定义的@RequestMapping("/api/v1/wechat"),所以我们自定义的完整接口是:/api/v1/wechat/api/v1/wechat,哎?有没有一点似曾相识的感觉?没有的话你可以看回一开始的application.properties所定义的redirect_url,你是不是已经发现我们自定义的接口和redirect_url一模一样,还记得我说redirect_url的作用吗?redirect_url就是把其url的值通过请求的方式发送给微信开发平台,告诉微信开发平台按照这个地址来回调code。这就解释了回调接口为什么是/api/v1/wechat/api/v1/wechat,不然网站后台就接收不到回调的code了。

喘口气~。

来看第二个问题:哪冒出来一个User和userService?

我先喘口气,因为重难点来了!!!这是我认为最难的一个点,但我一定尽力给你讲清楚了!

从代码的注释可以明显的知道 这行代码:

User user = userService.saveWeChatUser(code);//就是保存用户信息的。

但因为我把代码封装得还行,所以单从这行代码来看,你完全不知道这行代码内部在干什么。

我们从这行代码的左边开始看:

1、User

既然是保存信息,那肯定要有一个User的实体类对吧。

终于上代码~。

/*** 用户实体类*/
public class User implements Serializable {private Integer id;   //用户idprivate String openid; //不好说,我忘了private String name;  //昵称private String headImg; //头像地址private String phone;   //电话private String sign;    //我又忘了private Integer sex;    //性别private String city;    //城市private java.util.Date createTime;  //生成时间,也就是登陆时间public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getHeadImg() {return headImg;}public void setHeadImg(String headImg) {this.headImg = headImg;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getSign() {return sign;}public void setSign(String sign) {this.sign = sign;}public Integer getSex() {return sex;}public void setSex(Integer sex) {this.sex = sex;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public java.util.Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}
}

User实体类就没什么好说的了,只能说还有很大空间优化,但这不是目前的重点,先不用管。

既然有User实体类,那肯定是要有MyBatis框架与数据库映射

上代码~。

Path:src\main\java\com\springbootpay\mapper\UserMapper.java

public interface UserMapper {//mybatis参数注意取值⽤ #{} ,别⽤${},因为存在sql注⼊⻛险/*** 根据主键id查找* @param userId* @return*/@Select("select * from user where id = #{id}")User findByid(@Param("id") int userId);/*** 根据openid找用户* @param openid* @return*/@Select("select * from user where openid = #{openid}")User findByopenid(@Param("openid") String openid);/*** 保存新用户* @param user* @return*/@Insert("INSERT INTO `user` ( `openid`, `name`, `head_img`, `phone`, `sign`, `sex`, `city`, `create_time`)" +"VALUES" +"(#{openid},#{name},#{headImg},#{phone},#{sign},#{sex},#{city},#{createTime});")@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")int save(User user);}

说明:MyBatis的内容我不会过多展开讲(要陪女朋友的嘛,时间有限啊)

1、三部分代码的作用注释已经说明

2、是通过注解的方式映射数据库的增删改查,如@Select、@Insert,只要你学过Mysql,明显的Mysql语句嘛。

3、@Options(useGeneratedKeys=true, keyProperty=“id”, keyColumn=“id”),大概的意思就是选择表中字段名为id的值作为主键,自动增加,也就是每保存(增加)一个用户的信息,id自动加1。

**User user = userService.saveWeChatUser(code);**继续往右看

2、userService

service是业务层, 上面介绍MyBatis是属于数据的底层操作,什么是数据的底层操作?就是单纯的完成数据的增删改查,没有任何的业务操作。什么业务?数据的底层操作能单纯的完成数据的增删改查,但是要增加多少个?删除多少个?修改多少个?查找多少个?根据什么条件去增删改查,这就是业务,是要根据用户的实际需求去操作实现的。

因此,一个项目中理应有一个service层。

上代码,service接口类

Path:src\main\java\service\UserService.java

public interface UserService {//业务:保存用户信息User saveWeChatUser(String code);}

那接口写好了,下一步就是如何来实现这个接口。

在讲解如何实操实现接口之前,我们先来做一些准备:

●既然是保存用户信息,那必定要先拿到assess_token,但在上面的讲解中我们只是实现了:获取了回调的code!但还没去换取assess_token。

实现携带code、appid和appsecret换取assess_token

首先先来学习官网文档(“//”附详细解析):

通过code获取access_token//获取assess_token的url模板
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code参数说明参数         是否必须             说明
appid           是        应用唯一标识,在微信开放平台提交应用审核通过后获得
secret          是        应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
code            是        填写第一步获取的code参数
grant_type      是        填authorization_code返回说明正确的返回://返回的是json格式
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}参数说明参数                        说明
access_token         接口调用凭证
expires_in       access_token接口调用凭证超时时间,单位(秒)
refresh_token       用户刷新access_token
openid              授权用户唯一标识
scope            用户授权的作用域,使用逗号(,)分隔
unionid         当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。错误返回样例:{"errcode":40029,"errmsg":"invalid code"}

通过使用HttpClient4.x工具封装get和post方法获取assess_token,上代码~。

Path:D:\src\main\java\com\springbootpay\utils\HttpUtils.java

/*** 封装http get和post方法*/
public class HttpUtils {//在pom.xml引入Gson依赖,目的是为了把返回的Json数据格式化为Map集合,存储起来,也方便写入数据库private static  final Gson gson = new Gson();/*** get方法* @param url* @return*///需要传入一个参数:由code、appid和appsecret拼装好的请求assess_token的urlpublic static Map<String,Object> doGet(String url){//为存储返回的数据作准备,返回的是Json格式,适合用Key-Vaule的形式存储Map<String,Object> map = new HashMap<>();// 获得Http客户端(可以理解为:得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)CloseableHttpClient httpClient =  HttpClients.createDefault();RequestConfig requestConfig =  RequestConfig.custom()//设置连接超时时间(单位毫秒).setConnectTimeout(5000)//设置请求超时时间(单位毫秒).setConnectionRequestTimeout(5000)// socket读写超时时间.setSocketTimeout(5000)//设置是否允许重定向(默认为true).setRedirectsEnabled(true).build();// 创建Get请求HttpGet httpGet = new HttpGet(url);// 将上面的配置信息 运用到这个Get请求里httpGet.setConfig(requestConfig);try{// 由客户端执行(发送)Get请求HttpResponse httpResponse = httpClient.execute(httpGet);// 从响应模型中获取响应实体的状态码,例如:200表示成功if(httpResponse.getStatusLine().getStatusCode() == 200){//把响应的实体类转化为json字符串String jsonResult = EntityUtils.toString( httpResponse.getEntity());//把json字符串转化为map的key-value形式存储map = gson.fromJson(jsonResult,map.getClass());}}catch (Exception e){e.printStackTrace();}finally {//记得关闭客户端try {if(httpClient != null){httpClient.close();}}catch (Exception e){e.printStackTrace();}}//返回map,后面就可以通过map去取用户的基本信息return map;}/*** 封装post(但其实换取assess_token发送的是一个get请求,所以在这里封装post意义不大,但一个网站由很多部分结合的,虽然换取assess_token用不到post请求,但或许别的地方用到的呢对吧(如:微信支付),so还是值得一学的)* @return*/public static String doPost(String url, String data,int timeout){CloseableHttpClient httpClient =  HttpClients.createDefault();//设置RequestConfig requestConfig =  RequestConfig.custom().setConnectTimeout(timeout) //连接超时.setConnectionRequestTimeout(timeout)//请求超时.setSocketTimeout(timeout)  // socket读写超时时间.setRedirectsEnabled(true)  //允许自动重定向.build();HttpPost httpPost  = new HttpPost(url);httpPost.setConfig(requestConfig);//post请求:数据是放在请求头的。 所以要创建一个Header并配置一些参数httpPost.addHeader("Content-Type","text/html; chartset=UTF-8");if(data != null && data instanceof  String){ //使用字符串传参//把data转成实体类,然后通过UTF-8编码),最后放到post请求里StringEntity stringEntity = new StringEntity(data,"UTF-8");httpPost.setEntity(stringEntity);}try{//都是套路,参考get方法部分,不多赘述~。CloseableHttpResponse httpResponse = httpClient.execute(httpPost);HttpEntity httpEntity = httpResponse.getEntity();if(httpResponse.getStatusLine().getStatusCode() == 200){String result = EntityUtils.toString(httpEntity);return result;}}catch (Exception e){e.printStackTrace();}finally {try{httpClient.close();}catch (Exception e){e.printStackTrace();}}return null;}
}

以上,我们就封装好的获取assess_token的方法。再看回那行代码

User user = userService.saveWeChatUser(code); 继续往右看

3、saveWeChatUser

code、appid、appsecret以及或获取assess_token方法都准备好了,接下来就是实现saveWeChatUser();

4、saveWechatUser要实现的事情

1、接收code参数,和appid、appsecret拼串成换取assess_token的url。

2、调用封装好的get方法获取含用户基本信息的map(先获取到assess_token,再通过assess_token拼串去那用户信息)。

3、从map里获取用户的基本信息,并通过UserMapper存储到数据库。(要实现两次get请求,第二次的map才含用户的基本信息,然后存储入数据库,第一次get请求的map是为了拿assess_token)

上代码~。

Path:src\main\java\service\impl\UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {//自动注入WeChatConfig(为了拿appid、appsecret)、UserMapper(为了通过UserMapper存储到数据库)@Autowiredprivate WeChatConfig weChatConfig;private UserMapper userMapper;@Overridepublic User saveWeChatUser(String code) {//拼串成换取assess_token的url,先把url模板getOpenAccessTokenUrl放到WeChatConfig类里//获取assess_token的url模板:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_codeString accessTokenUrl = String.format(WeChatConfig.getOpenAccessTokenUrl(),weChatConfig.getOpenAppid(),weChatConfig.getOpenAppsecret(),code);//获取assess_tokenMap<String ,Object> baseMap =  HttpUtils.doGet(accessTokenUrl);//说明没拿到if(baseMap == null || baseMap.isEmpty()) {return  null;}//否则,不为null,就是拿到了。然后就拿token、openId(还要通过OpenID来获取用户基本信息)String accessToken = (String)baseMap.get("access_token");String openId  = (String) baseMap.get("openid");User dbUser = userMapper.findByopenid(openId);//如果不为空,说明已登录,直接返回Userif(dbUser!=null) { //更新用户,直接返回return dbUser;}//拼串成获取用户基本信息url,也要先把url模板getOpenUserInfoUrl放到WeChatConfig类里.//用户信息url模板:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENIDString userInfoUrl = String.format(WeChatConfig.getOpenUserInfoUrl(),accessToken,openId);//获取含用户信息的mapMap<String ,Object> baseUserMap =  HttpUtils.doGet(userInfoUrl);if(baseUserMap == null || baseUserMap.isEmpty()){return  null;}String nickname = (String)baseUserMap.get("nickname"); //获取昵称//回调的sex是Double类型Double sexTemp  = (Double) baseUserMap.get("sex");     int sex = sexTemp.intValue();                          //性别String province = (String)baseUserMap.get("province"); //省份String city = (String)baseUserMap.get("city");         //城市String country = (String)baseUserMap.get("country");   //国家//头像地址,显示图片要通过前后端的联调,一般是前端的事String headimgurl = (String)baseUserMap.get("headimgurl");//中国-广东-广州StringBuilder sb = new StringBuilder(country).append("-").append(province).append("-").append(city);String finalAddress = sb.toString();//解决乱码try {//这块我本人也不是很懂,我有空问问大力再来告诉你,嘻嘻~。nickname = new String(nickname.getBytes("ISO-8859-1"), "UTF-8");finalAddress = new String(finalAddress.getBytes("ISO-8859-1"), "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}//这,常规操作对吧!User user = new User();user.setName(nickname);user.setHeadImg(headimgurl);user.setCity(city);user.setOpenid(openId);user.setSex(sex);user.setCreateTime(new Date());userMapper.save(user);return user;}
}

绕了一大圈,终于把用户的基本信息拿到,好像。。。微信登录已经搞定了???(说真的我差点就去吃饭了。。。),给你一个脚丫子,醒醒!!!先看看我们是从那绕出来讨论的:

emmm…就是这里:微信登录的回调

@Controller
@RequestMapping("/api/v1/wechat")
public class WechatController {@Autowiredprivate WeChatConfig weChatConfig; //注入WeChatConfig@Autowiredprivate UserService userService;//有关微信回调的代码,不用管!后面会讲。/*** 拼装微信扫一扫登录url* @return*///为了方便讲解,忽略微信登录部分的代码//.............../*** 微信扫码登录,回调地址* @param code* @param state* @param response* @throws IOException*/@GetMapping("/user/callback") //回调接口public void wechatUserCallback(@RequestParam(value = "code",required = true) String code,String state, HttpServletResponse response) throws IOException {//保存用户信息User user = userService.saveWeChatUser(code);if(user != null){//通过jwt生成token返回给前端String token = JwtUtils.geneJsonWebToken(user);// state 当前用户的页面地址,需要拼接 http://  这样才不会站内跳转response.sendRedirect(state+"?token="+token+"&head_img="+user.getHeadImg()+"&name="+URLEncoder.encode(user.getName(),"UTF-8"));}}

我们从下面一句代码绕了出来:

//保存用户信息

User user = userService.saveWeChatUser(code);

既然已经解决了,那就继续看下去~。

下面,我们先不讨论这段代码,我们最后看,因为这段代码非微信登录开发必要。但我在这里添加肯定有其意义,等会说一下!

    if(user != null){//通过jwt生成token返回给前端String token = JwtUtils.geneJsonWebToken(user);

我们先看这一段:

回调页面地址。

// state 当前用户的页面地址,需要拼接 http://  这样才不会站内跳转
response.sendRedirect(state+"?token="+token+"&head_img="+user.getHeadImg()+"&name="+URLEncoder.encode(user.getName(),"UTF-8"));

还是拼串:传入state参数,登录成功后就能自动跳转登录前的页面了。但同时还需要附加上一些参数:head_img(头像地址)、name(用户昵称)等,根据实际需求而定。

为什么需要附加上一些参数,相信大家都认识过这样的场景:

大力登录前:

大力登录后:

前后对比,我大力的头像就出来了,这就是原因所在:返回给前端,前端通过联调等操作显示图片。

好像。。。。这次真的讲完了!!!奥力给~。

你说什么?上面那段代码还没讲?好!其实微信登录这块是真的已经完成了。

官方文档的时序图:

注意:并不是最标准的时序图,因为没有说到请求用户信息那块,总的来说就是不完整的。

而这段简短的代码,就要结合网站开发的实际来讨论了。

    if(user != null){//通过jwt生成token返回给前端String token = JwtUtils.geneJsonWebToken(user);

微信登录与网站开发的结合

上面说了很多,几乎都是如何实现微信扫一扫登录,现在登录方面是完成了,但怎么与网站联系在一起呢?我们的主题是什么? **网站开发之微信登录!**简单来说就是,我们网站如何知道用户通过微信登录了,这点很重要!因为登录成功与否涉及到用户的个人权限的问题,举个例子:你购买了一个学习视频,后台给你的权限是:必须是你的账号或微信登录才能观看。那问题就来了:你通过微信登录了,但网站不知道啊!网站如何知道你登录后的一切操作 就是 你登录后的操作呢?(有点绕,你细读哈~。)

这就涉及一个关于token(核心:类似一张身份证,说明你的身份,登录后的每一次操作都带上它去网站后台验证,以证明你登录过)方法的问题,关于token的具体方面我这里就不展开说了,可以自行理解学习,这里我就展示如何使用JWT实现(JWT也是自行了解)

封装JWT工具类

Path:

package com.springbootpay.utils;import com.springbootpay.domain.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;/*** jwt工具类*/
public class JwtUtils {public static final String SUBJECT = "dali";public static final long EXPIRE = 1000*60*60*24*7;//过期时间,毫秒,一周//秘钥public static final String APPSECRET = "zhugedali";/*** 生成jwt* @param user* @return*/public static  String geneJsonWebToken(User user){if(user == null || user.getId() == null || user.getName() == null|| user.getHeadImg() == null){return null;}String token = Jwts.builder().setSubject(SUBJECT).claim("id",user.getId()).claim("name",user.getName()).claim("img",user.getHeadImg()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis()+EXPIRE)).signWith(SignatureAlgorithm.HS256,APPSECRET).compact();return token;}/*** 校验token* @param token* @return*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APPSECRET).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {}return null;}
}
调用封装好的JWT工具的方法,生成token返回给前端
    if(user != null){//通过jwt生成token返回给前端String token = JwtUtils.geneJsonWebToken(user);

以上,就是我想和你们分享讨论的,关于网站开发的微信登录,完毕!

而我实际做的微信扫一扫登录中还加了本地域名映射工具Ngrock,和登录的拦截开发。

由于这都不是讨论重点,所以我就不展开,本地域名映射工具Ngrock适合像我这样的新手玩玩,毕竟通过映射后的域名不限于本地访问,还挺有意思(但其实自己不说域名装X,也没人知道,哈哈!),登录的拦截开发一般是后台权限管理的工作,开发起来也是很不容易,所以在这就不展开了。

最后还是要强调一下:仅仅是开发流程和一些知识点,如果你问我那数据库那些怎么配置等等?别问,问就是不知道哈!

最后的最后,

有时间我会继续分享以下内容,本人是真的小白,非常欢迎大家指正!

《一起学习网站开发之基于Spring boot的微信支付开发流程和知识点》

《一起学习网站开发之基于Spring boot的shiro权限框架开发流程和知识点》

。。。。

一起学习网站开发之基于Spring boot的微信登录开发流程和知识点相关推荐

  1. 基于Spring Boot的社区论坛开发

    基于Spring Boot的社区论坛开发 第一章:框架基本介绍+前端页面+环境搭建 1.介绍 所有框架: SSM开发框架:Spring(一套JAVA框架).Spring MVC(处理浏览器请求).My ...

  2. 基于Spring Boot垂钓服务系统的设计与实现毕业设计源码071739

    目  录 摘要 1 绪论 1.1 研究背景 1.2研究意义 1.3相关技术介绍 1.4论文结构与章节安排 2垂钓服务系统需求分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性 ...

  3. 基于Spring boot的个人理财系统

    基于Spring boot的个人理财系统 开发环境: Eclipse/MyEclipse.Tomcat8.Jdk1.8 数据库: MySQL 技术: Springboot+Mybatis+Spring ...

  4. 基于Spring boot框架开发的电商网站系统

    目 录 第一章 绪论- 2 1.1 编写目的 - 2 1.2 项目背景 - 2 1.3 项目需求 - 2 第二章 系统体系结构 - 3 2.1 系统体系结构 - 3 2.2 数据库设计 E-R 图 - ...

  5. 基于Spring Boot 2 和 Vue.js 2 的 食品科学与工程学院网站的设计与实现

    摘要 互联网具有传播信息容量大.形态多样.迅速方便.自由和交互等特点,已经发展成为新的传播媒体,现在很多的大学和社会其他部门都已经建立了网站,通过计算机网络实现宣传.交流及资源的整合.建立学校网站有以 ...

  6. 基于Spring boot开发电子宿舍管理系统毕业设计源码132056

    摘  要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用.信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代 ...

  7. (附源码)springboot+mysql+基于Spring boot开发电子宿舍管理系统 毕业设计132056

    摘 要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用.信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代. ...

  8. springboot+mysql+基于Spring boot开发电子宿舍管理系统 毕业设计-附源码132056

    摘  要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用.信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代 ...

  9. (附源码)基于Spring boot开发电子宿舍管理系统 毕业设计132056

    摘 要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用.信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代. ...

  10. 基于Spring Boot和Spring Cloud实现微服务架构学习--转

    原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...

最新文章

  1. 第五六周机电传动控制作业
  2. 对Multi-bin 技术的理解
  3. python如何改变入参的值_从事数据分析3年后,发现用python入门数据分析这三本书必看!...
  4. mysql建立索引的优缺点|创建索引alter或create索引分类(PRIMARY KEY,UNIQUE KEY,FULLTEXT,INDEX)作用查看索引show index from table
  5. 宝宝树发行价为6.8港元 11月27日在港交所正式挂牌
  6. 生成微信蓝色昵称,原来如此简单
  7. 录屏 模拟器_系统自带的录屏好用吗?其实有功能更强大的软件
  8. GitLab CI/CD conda: command not found
  9. Delphi Open Tools Api实例研究(二)
  10. java hibernate映射_java – Hibernate教程 – 在哪里放置映射文件?
  11. QT 周立功 can分析仪 二次开发
  12. 单片机 cror crol
  13. python PIL 图像处理库简介(一)
  14. 微信小程序 关于头像上传,showActionSheet,chooseImage,uploadFile
  15. 【U8+】数据库清空U8+admin密码
  16. Python环境下用中文做了个《王者荣耀》AI脚本,附视频
  17. javascript小案例-----tab栏切换
  18. OpenDaylight系类教程(十二)-- Release Notes
  19. 用计算机控制操作照相机,摄影教程:用电脑控制相机远程拍摄
  20. 14个步骤让你快速学会UG!

热门文章

  1. 影响力最大化 RIS 反向影响力采样算法
  2. Diy-Scratch(4) 大家来找茬
  3. 喜报!易基因“同源基因特异性甲基化时序数据分析方法”获专利授权
  4. thinkphp+vue傻瓜式PS级可视化自助建站系统平台版
  5. 大龄程序员的一些出路
  6. android sd 权限修改,真正免root的sd卡权限修改软件详细使用教程
  7. Android 自动朗读TT
  8. win10计算机休眠设置在哪里,win10怎么让屏幕一直亮着 win10设置休眠时间详细教程...
  9. 陈天出席华盛顿大学春季招聘会 | ArcBlock 动态
  10. 计算机交叉专业考研方向,与理工科交叉的计算机专业考研方向有哪些?