VUE+SpringBoot前后端实现网页微信扫码登录


官方文档:

https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

一、前端部分

1、首先在标签内添加如下js文件

<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

2、在html中定义一个div标签,用来展示二维码

<div id="qrCode"></div>

3、Vuejs代码如下

methods:{
    // 初始化微信二维码
    initQRCode() {
       let style = `.old-template{width:100%;height:100%;}
            .loginPanel,.impowerBox{width:100%;height:100%;}
            .loginPanel{display: flex;flex-direction: column;}
            .panelContent{flex:1;height:0;display: flex;flex-direction: column;color:#333;}
            .panelContent .wrp_code{flex:1;height:0;display: flex;align-items: center;justify-content: center;}
            .panelContent .wrp_code img {margin-top:0;}
            .impowerBox .qrcode {width:60%;}
            .loginPanel .title {display:none;}
            .loginPanel .info {width:100%;display: flex;justify-content: center;align-items: center;}`; //更改二维码样式
        let blob = new Blob([style], {type: 'text/css;chartset=utf-8'})
        let fileReader = new FileReader();
        fileReader.readAsDataURL(blob);
        fileReader.onload = e => {
            new WxLogin({
                id: "qrCode",
                appid: 'xxxxxxxxx',
                scope: 'snsapi_login',
                redirect_uri: encodeURIComponent('http://localhost:8080'), //如果有项目名,在这里进行拼接,微信开放平台设置授权回调域,不用填写项目名
                style: 'white',
                state: "",
                href: e.target.result
            })
        }
    },
    // 微信二维码登录
    QRCodeService(code) {
        ajaxMethods.defaultRequest("weChatOpenPlatform/login", {
            code
        }, msg => {
            const {status, expires_in, userId, username, headimgurl, authority} = msg;
            if (authority) {
                ajaxMethods.message(status, null, "登录");
                if (status) {
                    commonMethods.setCookies({
                        userId,
                        cloudUserName: username,
                        headimgurl
                    }, new Date(new Date().getTime() + expires_in));
                }
            } else {
                this.$message.error("您没有权限,请联系管理员")
            }
        }, null, null, () => {
            setTimeout(() => {
                location.href = location.origin + location.pathname + location.hash;
            }, 1000)
        })
    }
},   
mounted(){
    let code = location.search;
    code = code.substring(code.lastIndexOf('code=') + 5);
    code = code.substring(0, code.lastIndexOf('&'));
    if (code) {
        this.QRCodeService(code);
    } else {
        this.initQRCode();
    }
}

参数说明

参数是否必须说明
self_redirecttrue:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。
id第三方页面显示二维码的容器id
appid应用唯一标识,在微信开放平台提交应用审核通过后获得
scope应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
redirect_uri重定向地址,需要进行UrlEncode
state用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
style提供"black"、"white"可选,默认为黑色文字描述。详见文档底部FAQ
href自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ

后端部分

1、通过前端传递过来的code获取access_token

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

返回说明

正确的返回:

{ 
    "access_token":"ACCESS_TOKEN", 
    "expires_in":7200, 
    "refresh_token":"REFRESH_TOKEN",
    "openid":"OPENID", 
    "scope":"SCOPE",
    "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
返回结果参数
参数说明
access_token接口调用凭证
expires_inaccess_token接口调用凭证超时时间,单位(秒)
refresh_token用户刷新access_token
openid授权用户唯一标识
scope用户授权的作用域,使用逗号(,)分隔
unionid当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。

错误返回样例:

{"errcode":40029,"errmsg":"invalid code"}

2、通过access_token调用接口

获取access_token后,进行接口调用,有以下前提:

  1. access_token有效且未超时;
  2. 微信用户已授权给第三方应用帐号相应接口作用域(scope)。

对于接口作用域(scope),能调用的接口有以下:

授权作用域(scope)接口接口说明
snsapi_base/sns/oauth2/access_token通过code换取access_token、refresh_token和已授权scope
snsapi_base/sns/oauth2/refresh_token刷新或续期access_token使用
snsapi_base/sns/auth检查access_token有效性
snsapi_userinfo/sns/userinfo获取用户个人信息

3、后端Java代码如下:

application.yml

#微信操作
wechat:
    #开放平台
    openPlatform:
	    appId: xxxxxx
        appSecret: xxxxxx
        #Get请求  参数?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        #参数            是否必须       说明
        #appid         是          应用唯一标识,在微信开放平台提交应用审核通过后获得
        #secret            是          应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
        #code          是          填写第一步获取的code参数
        #grant_type        是          填authorization_code
        accessTokenUrl: https://api.weixin.qq.com/sns/oauth2/access_token
        #获取用户信息
        userInfoUrl: https://api.weixin.qq.com/sns/userinfo

WeChatOpenPlatformController

@RestController
@Api(tags = "微信开放平台操作接口")
@RequestMapping("/weChatOpenPlatform")
public class WeChatOpenPlatformController {

    @Autowired
    WeChatOpenPlatformService weChatOpenPlatformService;


    @ApiOperation(value = "微信扫码登录")
    @PostMapping("/login")
    @ApiImplicitParam(name = "code", value = "code,用户扫码获取的code", dataTypeClass = String.class, required = true)
    public JSONObject login(String code) {
        return weChatOpenPlatformService.login(code);
    }

    @ApiOperation(value = "退出微信登录")
    @PostMapping("/logout")
    @ApiImplicitParam(name = "userId", value = "用户ID", dataTypeClass = String.class, required = true)
    public boolean logout(String userId) {
        return weChatOpenPlatformService.logout(userId);
    }
}

WeChatOpenPlatformService

public interface WeChatOpenPlatformService {
    JSONObject login(String code);

    boolean logout(String userId);
}

WeChatOpenPlatformServiceImpl

返回结果根据项目自行处理:

@Service
public class WeChatOpenPlatformServiceImpl implements WeChatOpenPlatformService {

    @Value("${wechat.openPlatform.appId}")
    private String appId;
    @Value("${wechat.openPlatform.appSecret}")
    private String appSecret;
    @Value("${wechat.openPlatform.accessTokenUrl}")
    private String accessTokenUrl;
    @Value("${wechat.openPlatform.userInfoUrl}")
    private String userInfoUrl;

    @Autowired
    UsersMapper usersMapper;

    /**
     * 微信扫码登录
     *
     * @param code code
     * @return 用户信息
     */
    @Override
    public JSONObject login(String code) {
        JSONObject tokenJSONObject = getAccessToken(code);
        System.out.println(tokenJSONObject);
        String access_token = tokenJSONObject.getString("access_token");
        JSONObject userInfo = new JSONObject();
        userInfo.put("authority", false);
        userInfo.put("status", false);
        if (access_token != null) {
            Integer expires_in = tokenJSONObject.getInteger("expires_in");
            String openid = tokenJSONObject.getString("openid");
            String unionid = tokenJSONObject.getString("unionid");

            ToolUtils.setSession("accessToken", access_token, expires_in);
            userInfo = getWeChatUserInfo(access_token, openid);

            System.out.println(userInfo);
            userInfo.put("expires_in", expires_in);

            Users users = new Users();
            users.setUnionid(unionid);
            List<Users> usersList = usersMapper.unIonIdList(users);
            Users users1;
            if (usersList.size() > 0) {
                users1 = usersList.get(0);
                userInfo.put("status", true);
                userInfo.put("authority", users1.getStatus());
            } else {
                users1 = new Users();
                users1.setId(UUID.randomUUID().toString());
                users1.setUsername(userInfo.getString("nickname"));
                users1.setUnionid(unionid);
                users1.setPassword(EncryptUtils.MD5("admin"));
                int i = usersMapper.insertSelective(users1);
                userInfo.put("status", i==1);
            }
            userInfo.put("userId", users1.getId());
            userInfo.put("username", users1.getUsername());
        }
        return userInfo;
    }

    /**
     * 退出微信登录
     *
     * @param userId 用户ID
     * @return 状态  成功true、则false
     */
    @Override
    public boolean logout(String userId) {
        Users users = usersMapper.selectByPrimaryKey(userId);
        if (users != null) {
            ToolUtils.removeSession("accessToken");
            return true;
        }
        return false;
    }

    /**
     * 获取token
     *
     * @param code code
     * @return access_token
     */
    public JSONObject getAccessToken(String code) {
        Map<String, Object> map = new HashMap<>();
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("code", code);
        map.put("grant_type", "authorization_code");
        String params = HttpUtil.get(accessTokenUrl, map);
        return JSONObject.parseObject(params);
    }

    /**
     * 获取微信用户信息
     *
     * @param access_token token
     * @param openId       openid
     * @return 用户信息
     */
    public JSONObject getWeChatUserInfo(String access_token, String openId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("access_token", access_token);
        map.put("openid", openId);
        map.put("lang", "zh_CN");
        String params = HttpUtil.get(userInfoUrl, map);
        return JSONObject.parseObject(params);
    }
}

ToolUtils

工具类:

@Component
public class ToolUtils {

    public static final int COOKIE_DEFAULT_TIME = 1;  //以秒为单位(1s)

    public static final int SESSION_DEFAULT_TIME = 1;  //以秒为单位(1s)

    public static HttpServletRequest request;

    public static HttpServletResponse response;

    public static HttpSession session;

    @Autowired
    private HttpServletResponse httpServletResponse;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private HttpSession httpSession;

    @PostConstruct
    public void beforeInit() {
        request = httpServletRequest;
        response = httpServletResponse;
        session = httpSession;
    }

    /**
     * 添加cookie
     *
     * @param key       名称
     * @param value     值
     * @param cookieAge 过期时间
     */
    public static void setCookie(String key, String value, int cookieAge) {
        if (key == null) {
            return;
        }
        if (value == null) {
            value = "";
        }

        Cookie cookie = new Cookie(key, value);
        cookie.setPath("/");
        if (cookieAge != 0) {
            cookie.setMaxAge(COOKIE_DEFAULT_TIME * cookieAge);
        } else {
            cookie.setMaxAge(COOKIE_DEFAULT_TIME);
        }
        response.addCookie(cookie);
        try {
            response.flushBuffer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取cookie
     *
     * @param cookieName 名称
     * @return 值
     */
    public static String getCookie(String cookieName) {
        Cookie[] cookies = request.getCookies();
        String result = null;
        if (null != cookies && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(cookieName)) {
                    result = cookie.getValue();
                    break;
                }
            }
        }
        return result;
    }

    /**
     * 删除cookie
     *
     * @param cookieName 名称
     * @return 结果
     */
    public static boolean removeCookie(String cookieName) {
        boolean result = false;
        if (null != cookieName) {
            Cookie cookie = new Cookie(cookieName, null);
            cookie.setMaxAge(0);
            cookie.setPath("/");
            cookie.setValue("");
            response.addCookie(cookie);
            result = true;
        }
        return result;
    }

    /**
     * 设置session
     *
     * @param key        键
     * @param value      值
     * @param sessionAge 过期时间
     */
    public static void setSession(String key, String value, int sessionAge) {
        if (key == null) {
            return;
        }
        if (value == null) {
            value = "";
        }
        session.setAttribute(key, value);
        if (sessionAge != 0) {
            session.setMaxInactiveInterval(COOKIE_DEFAULT_TIME * sessionAge);
        } else {
            session.setMaxInactiveInterval(SESSION_DEFAULT_TIME);
        }
    }

    /**
     * 获取session
     *
     * @param sessionName 键
     * @return 返回内容
     */
    public static String getSession(String sessionName) {
        Object attribute = session.getAttribute(sessionName);
        return null != attribute ? attribute.toString() : null;
    }

    /**
     * 删除session
     *
     * @param sessionName 键
     */
    public static void removeSession(String sessionName) {
        Object attribute = session.getAttribute(sessionName);
        if (null != attribute) {
            session.removeAttribute(sessionName);
        }
    }
}

以上代码亲测可用,如有问题欢迎留言!

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值