vue+springboot微信公众网页项目开发及腾讯云服务器部署

vue+springboot微信公众网页项目开发及腾讯云服务器部署

前言

​ 本项目前端采用vue-cli2脚手架生成,vscode编辑;后端idea编辑。微信公众网页开发,只需在公众平台配置回调域名等相关操作(后面会有介绍),其他与正常开发相同。

​ 本系列文章只提供微信公众号开发到上线环境配置的调通,开发内容略少,感谢网上各位博主的分享,让我能够顺利完成项目的开发与文档的编写。

(一)前期开发准备

  • vue项目创建

    npm install -g vue-cli
    vue init webpack
    
  • springboot项目创建

    使用idea快速创建。教程见:点击查看

  • 微信公众平台测试号申请
    在这里插入图片描述
    在微信公众平台申请测试号。申请成功后,在跳转页面中填写以下信息:

    在这里插入图片描述
    在这里插入图片描述

两个域名进行修改。第二章图片点击网页账号栏目右侧的修改按钮进行域名修改。完成后就可以进行网页授权和js-sdk的使用了。

内网透射(测试域名):点击查看

(二)vue前端项目开发

  • host检查

    可以在build目录下的webpack.dev.conf.js文件,devServer下添加disableHostCheck: true,跳过检查
    在config目录下修改index.js文件的host,这个默认是localhost,可修改

  • 闭门造车,干啥?免费开源的组件不香嘛。移动端组件推荐:vant , Mint UI

npm install less-loader@5.0.0 less@3.9.0 --save-dev
只有安装了之后,才能选择样式为less哦。要指定版本,不然会因为node版本过高或者webpack版本问题出错哦。
--save-dev 指把依赖安装在开发环境中,项目打包后不会添加进去。
  • 调用后端api

    为了方便 封装请求

    request.js文件
    import axios from 'axios'
    import { Toast } from 'vant'
    
    // create an axios instance
    const service = axios.create({
      baseURL: 'https://ip:5000',
      // withCredentials: true, // send cookies when cross-domain requests
      timeout: 5000 // request timeout
    })
    
    // // request interceptor
    service.interceptors.request.use(
      config => {
        // do something before request is sent
        return config
      },
      error => {
        // do something with request error
        console.log(error) // for debug
        return Promise.reject(error)
      }
    )
    
    // // response interceptor
    service.interceptors.response.use(
      /**
       * If you want to get http information such as headers or status
       * Please return  response => response
      */
    
      /**
       * Determine the request status by custom code
       * Here is just an example
       * You can also judge the status by HTTP Status Code
       */
      response => {
        const res = response.data
    
        // if the custom code is not 20000, it is judged as an error.
        // if (res.code !== 200) {
        //   Toast({
        //     message: res.message || 'Error',
        //     duration: 1500
        //   })
        //   return Promise.reject(new Error(res.message || 'Error'))
        // } else {
        //   return res
        // }
        return res
      },
      error => {
        console.log('err' + error) // for debug
        Toast({
          message: 'Error',
          duration: 1500
        })
        return Promise.reject(error)
      }
    )
    
    export default service
    
    
  • 小tips

    vue2 assets访问: require(‘url’)

    vant组件样式修改(样例): .van-field /deep/ .van-field__label{}

(三)springboot后端项目开发

  • 为了避免和前端项目启动端口冲突,更改后端端口。
application.yml文件(更改properties后缀名)
······
server:
  port: 5000
······
  • 为了解决跨域问题,后端配置

    package com.fgs.common.config;
    
    
    import org.apache.commons.lang.StringUtils;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Configuration
    public class CorsFilter implements Filter {
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
            HttpServletRequest request = (HttpServletRequest) req;
    
            // 不使用*,自动适配跨域域名,避免携带Cookie时失效
            String origin = request.getHeader("Origin");
            if(StringUtils.isNotBlank(origin)) {
                response.setHeader("Access-Control-Allow-Origin", origin);
            }
    
            // 自适应所有自定义头
            String headers = request.getHeader("Access-Control-Request-Headers");
            if(StringUtils.isNotBlank(headers)) {
                response.setHeader("Access-Control-Allow-Headers", headers);
                response.setHeader("Access-Control-Expose-Headers", headers);
            }
    
            // 允许跨域的请求方法类型
            response.setHeader("Access-Control-Allow-Methods", "*");
            // 预检命令(OPTIONS)缓存时间,单位:秒
            response.setHeader("Access-Control-Max-Age", "3600");
            // 明确许可客户端发送Cookie,不允许删除字段即可
            response.setHeader("Access-Control-Allow-Credentials", "true");
    
            chain.doFilter(request, response);
        }
    
        @Override
        public void init(FilterConfig filterConfig) {
    
        }
    
        @Override
        public void destroy() {
        }
    }
    
    

(四)微信公众号网页授权获取openid和用户信息

微信js-sdk引入(npm安装):点击查看安装方式 本项目没有用到js-sdk 仅在此说明如何安装

建立三个文件。注意:使用resttemplate来调用外部api需要在启动项进行一下配置

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}
package com.fgs.application.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fgs.domain.entity.Oauth2Token;
import com.fgs.domain.entity.WxUserDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@RestController
@Slf4j
public class WxApi {

    @Autowired
    private RestTemplate restTemplate;

    private static final String APP_ID = "";
    private static final String APP_SECRET = "";

    private static final String BACK_URL = "回调地址";

	// 前端获取返回的url来访问获取code  前端:window.location.href = url
    @RequestMapping("/wxLoginInit")
    public String loginInit(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
                + APP_ID
                + "&redirect_uri="
                + URLEncoder.encode(BACK_URL, "UTF-8")
                + "&response_type=code"
                + "&scope=snsapi_base"
                + "&state=STATE#wechat_redirect" ;
       return url;
    }

	// 获取前端得到code获取信息
    @RequestMapping("/wxLogin")
    public String wxLogin(@RequestParam("code") String code) {
        log.info("code: " + code);
        String openId = "";
        Oauth2Token oauth2Token = getOauth2Token(APP_ID, APP_SECRET, code);
        String accessToken = oauth2Token.getAccessToken();
        // 用户标识
        openId = oauth2Token.getOpenId();
        // 获取用户信息
        WxUserDO wxUserInfo = getWxUserInfo(accessToken, openId);
        openId = wxUserInfo.getOpenId();

        // 具体业务 start
        // 具体有什么业务需求自己实现,我这里只把 Oauth2Token 和 WxUserDO 的信息输出
        // ...
        log.info(oauth2Token.toString());
        log.info("用户标识openId:" + openId);
        log.info(wxUserInfo.toString());
        return openId;
    }

    private WxUserDO getWxUserInfo(String accessToken, String openId) {
        log.info("获取用户信息 开始...");
        log.info("网页授权接口访问凭证AccessToken:" + accessToken);
        log.info("用户标识openId:" + openId);

        WxUserDO wxUserInfo = null;

        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
        requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken);
        requestUrl = requestUrl.replace("OPENID", openId);

        log.info("拼接请求地址:" + requestUrl);

        // 通过网页授权获取用户信息
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(requestUrl, String.class);
        String body = responseEntity.getBody();
        JSONObject jsonObject = JSON.parseObject(body);
        if (null != jsonObject) {
            try {
                wxUserInfo = new WxUserDO();
                wxUserInfo.setOpenId(jsonObject.getString("openid"));
                wxUserInfo.setNickname(jsonObject.getString("nickname"));
                wxUserInfo.setSex(jsonObject.getInteger("sex"));
                wxUserInfo.setHeadimgurl(jsonObject.getString("headimgurl"));
            }catch (Exception e) {
                wxUserInfo = null;
                int errorCode = jsonObject.getInteger("errcode");
                String errorMsg = jsonObject.getString("errmsg");
                log.error("获取用户信息失败  errcode:{} errmsg:{}",errorCode,errorMsg);
            }
        }

        log.info("获取用户信息 结束...");
        return wxUserInfo;
    }

    private Oauth2Token getOauth2Token(String appId, String appSecret, String code) {
        log.info("获取网页授权凭证 开始...");

        Oauth2Token auth = null;

        //拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        requestUrl = requestUrl.replace("APPID", appId);
        requestUrl = requestUrl.replace("SECRET", appSecret);
        requestUrl = requestUrl.replace("CODE", code);

        log.info("拼接后的请求地址为:" + requestUrl);
        //获取网页授权凭证
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(requestUrl, String.class);
        String body = responseEntity.getBody();
        JSONObject jsonObject = JSON.parseObject (body);
        if(null != jsonObject) {
            try {
                auth = new Oauth2Token();
                auth.setAccessToken(jsonObject.getString("access_token"));
                auth.setExpiresIn(jsonObject.getInteger("expires_in"));
                auth.setRefreshToken("refresh_token");
                auth.setOpenId("openid");
                auth.setScope("scope");
            }catch(Exception e) {
                auth = null;
                int errorCode = jsonObject.getInteger("errcode");
                String errorMsg = jsonObject.getString("errmsg");
                log.error("获取网页授权凭证失败 errcode:{} errmsg:{}",errorCode,errorMsg);
            }
        }

        log.info("获取网页授权凭证 结束...");
        return auth;
    }

}

package com.fgs.domain.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Oauth2Token {
    private String accessToken; //网页授权接口调用凭证
    private int expiresIn; //凭证有效时长
    private String refreshToken; //用于刷新凭证
    private String openId; //用户标识
    private String scope; //用户授权作用域

    @Override
    public String toString() {
        return "Oauth2Token [accessToken=" + accessToken + ", expiresIn=" + expiresIn + ", refreshToken=" + refreshToken
                + ", openId=" + openId + ", scope=" + scope + "]";
    }
}

package com.fgs.domain.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class WxUserDO implements Serializable {
    private static final long serialVersionUID = -8881505873506562352L;
    private Integer id; //主键 id
    private String openId; //用户主键
    private String nickname; //用户昵称
    private Integer sex; //性别(1:男,2:女,0:未知)
    private String headimgurl; //用户头像链接

    //get 和 set 方法

    @Override
    public String toString() {
        return "WxUserDO [id=" + id + ", openId=" + openId + ", nickname=" + nickname + ", sex=" + sex + ", country="
                + ", province=" +  ", city=" + ", headimgurl=" + headimgurl + ", unionid="
                + ", privilegeList=" +  "]";
    }
}

(五)腾讯云服务器部署(windows 2012 server)

在这里插入图片描述

或直接在项目根目录下cmd命令:mvn clean package

  • 启动服务器上项目

    将打包后的dist文件夹下的文件移动到nginx文件夹中html文件夹中,springboot打包的jar包随意放。

    # 进入nginx安装文件夹
    start nginx
    # 进入jar包所在文件夹
    java -jar jar包
    

    启动成功后就可以进行访问了

(六)springboot https访问

因为nginx代理了vue访问,在vue访问后端api时 可能也需要https请求访问。因此配置证书,这里的证书去腾讯云服务器里下载。压缩包里tomcat文件夹,然后将.jks文件放在与application.yml文件同级目录下

application.yml
server:
  port: 5000
  ssl:
    key-store: classpath:域名.jks
    key-store-type: JKS
    key-store-password: 密码
    key-password: 密码

http转https,这里如果是用nginx代理vue前端项目就用不到。如果是将vue项目放在springboot的static下,会用到

/**
     * http重定向到https
     * @return
     */
    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                SecurityConstraint constraint = new SecurityConstraint();
                constraint.setUserConstraint("CONFIDENTIAL");
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*");
                constraint.addCollection(collection);
                context.addConstraint(constraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(httpConnector());
        return tomcat;
    }

    @Bean
    public Connector httpConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        //Connector监听的http的默认端口号
        connector.setPort(5000);
        connector.setSecure(false);
        //监听到http的端口号后转向到的https的端口号,也就是项目配置的port
        connector.setRedirectPort(5000);
        return connector;
    }
 tomcat.addAdditionalTomcatConnectors(httpConnector());
    return tomcat;
}

@Bean
public Connector httpConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setScheme("http");
    //Connector监听的http的默认端口号
    connector.setPort(5000);
    connector.setSecure(false);
    //监听到http的端口号后转向到的https的端口号,也就是项目配置的port
    connector.setRedirectPort(5000);
    return connector;
}









  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值