若依框架 + uniApp 实现微信小程序授权登录(附源码)

1 前言

通过若依框架实现微信小程序的授权登录。文章末尾提供源码下载

2.小程序代码

  • 微信开发者工具基础库用的2.26.2
  • 修改config.jsbaseUrl
baseUrl: 'http://localhost:8080',
  • app模块配置微信登录
    在这里插入图片描述

2.1 新增按钮微信授权登录

  • 在登录按钮下,新增微信授权登录按钮
<button @click="wxHandleLogin" class="login-btn cu-btn block bg-green lg round">微信授权登录</button>

2.2 创建wx.Login和wxHandleLogin方法

  • 调用uni.getProvider获取服务商信息
  • 调用uni.login获取code,并保存
  • 调用uni.getUserInfo获取iv和encryptedData,并保存
  • codeivencryptedData发送到后端,让后端处理
            // 微信登录方法        
            wxLogin(){
                  //获取服务商信息
                  uni.getProvider({
                    service: "oauth",
                            success: (res) => {
                                console.log(res);
                                if(~res.provider.indexOf("weixin")){
                                    //登录
                                    uni.login({
                                        provider: "weixin",
                                        success: (loginRes) => {
                                            console.log("登录",loginRes);
                                            this.wxLoginForm.code = loginRes.code;
                                        }
                                    })
                                }
                            }
                  })
              },	    
	    //微信授权登录
	  async wxHandleLogin() {
		  console.log("微信小程序发起授权登录")
		  this.$modal.loading("登录中,请耐心等待...")
		  //获取用户信息
		  uni.getUserProfile({
			desc:"获取用户信息",
		  	success: (resInfo) => {
		  		console.log("用户信息",resInfo)
		  		//设置偏移量和加密数据
		  		this.wxLoginForm.encryptedIv = resInfo.iv;
		  		this.wxLoginForm.encryptedData = resInfo.encryptedData;
		  		//向后端发起登录请求 TODO
		  		this.sendWxLoginFormToLocalService();
		  	},
		  	fail: (res) => {
		  		console.log("获取用户信息失败!",res)
		  	}
		  }) 
	  },

create钩子函数中调用

created() {
      this.getCode()
	  this.wxLogin()
    },

2.3 在@/api/login.js中新增wxLogin

  • 参数分别是codeencryptedIvencryptedData,需要后端提供接口,地址为/wxLogin,请求方式为post
// 微信登录授权
export function wxLogin(code, encryptedIv, encryptedData) {
  const data = {
    code,
    encryptedIv,
    encryptedData
  }
  return request({
    'url': '/wxLogin',
    headers: {
      isToken: false
    },
    'method': 'post',
    'data': data
  })
}

2.4 在@/store/modules/user.js下新增WxLogin

  • 引入api下的wxLogin
import { login, wxLogin, logout, getInfo } from '@/api/login'
  • actions中新增WxLogin
// 微信登录
	WxLogin({ commit }, wxUserInfo) {
	  const code = wxUserInfo.code
	  const encryptedIv = wxUserInfo.encryptedIv
	  const encryptedData = wxUserInfo.encryptedData
	  return new Promise((resolve, reject) => {
	    wxLogin(code, encryptedIv, encryptedData).then(res => {
	      setToken(res.token)
	      commit('SET_TOKEN', res.token)
	      resolve()
	    }).catch(error => {
	      reject(error)
	    })
	  })
	},

2.5 创建sendWxLoginFormToLocalService方法

  • 全局使用this.$store,需在App.vue中引入
import store from '@/store'
  • 新增sendWxLoginFormToLocalService
  • 调用action中的WxLogin
	  //向本地服务发起请求
	  sendWxLoginFormToLocalService(){
		console.log("向后端发起请求" + this.wxLoginForm);
		this.$store.dispatch('WxLogin', this.wxLoginForm).then(() => {
		  this.$modal.closeLoading()
		  //登录成功后的逻辑 TODO
		  console.log("登录成功后的逻辑!")
		  this.loginSuccess()
		}).catch(() => {
			console.log("微信登录失败,请重新登录!")
			this.$modal.msgError("微信登录失败,请重新登录!");
		})  
	  },

3.后端代码

3.1 yml配置文件中新增微信小程序id和秘钥

  • 新增appIdappSecret
wx-app:
  appId: wx32g4hdf54g51j56y
  appSecret: 65fhsd6fh595sdh2s6dgf6jg6f2sdjsj
  • 新增配置类WxAppConfig
package com.ruoyi.common.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "wx-app")
public class WxAppConfig {

    /** AppId */
    private String appId;

    /** AppSecret */
    private String appSecret;

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public void setAppSecret(String appSecret) {
        this.appSecret = appSecret;
    }
}

3.2 在数据库中新增open_id和union_id字段

在这里插入图片描述

3.3 在SysUser中新增两个字段

	/** unionId */
    private String unionId;

    /** openId */
    private String openId;
    
	public String getUnionId() {
        return unionId;
    }

    public void setUnionId(String unionId) {
        this.unionId = unionId;
    }

    public String getOpenId() {
        return openId;
    }

    public void setOpenId(String openId) {
        this.openId = openId;
    }

3.4 新增WxLoginBody接受小程序传来的数据

package com.ruoyi.common.core.domain.model;

public class WxLoginBody {

    /** 临时登录凭证 code 只能使用一次 */
    private String code;

    /** 偏移量 */
    private String encryptedIv;

    /** 加密数据 */
    private String encryptedData;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getEncryptedIv() {
        return encryptedIv;
    }

    public void setEncryptedIv(String encryptedIv) {
        this.encryptedIv = encryptedIv;
    }

    public String getEncryptedData() {
        return encryptedData;
    }

    public void setEncryptedData(String encryptedData) {
        this.encryptedData = encryptedData;
    }

    @Override
    public String toString() {
        return "WxLoginBody{" +
                "code='" + code + '\'' +
                ", encryptedIv='" + encryptedIv + '\'' +
                ", encryptedData='" + encryptedData + '\'' +
                '}';
    }
}

3.5 在ApplicationConfig类下注入RestTemplate

  • 发送请求用
/**
     * 远程调用
     * @return
     */
    @Bean
    public RestTemplate restTemplate()
    {
       return new RestTemplate();
    }

3.6 LoginUser中添加openId和unionId

	/** unionId */
    private String unionId;

    /** openId */
    private String openId;

3.7 SysUserMapper新增selectWxUserByOpenId

 /**
     * 根据openId查询用户信息
     * @param openId
     * @return
     */
    public SysUser selectWxUserByOpenId(String openId);

3.8 SysUserMapper.xml新增selectWxUserByOpenId

<select id="selectWxUserByOpenId" parameterType="String" resultMap="SysUserResult">
		<include refid="selectUserVo"/>
		where u.open_id = #{openId} and u.del_flag = '0'
</select>

3.9 SysLoginController新增接口wxLogin

package com.ruoyi.web.controller.system;

@Autowired
private RestTemplate restTemplate;

@Autowired
private WxAppConfig wxAppConfig;

/**
 * 登录验证
 *
 * @author ruoyi
 */
@RestController
public class SysLoginController
{
	@PostMapping("/wxLogin")
    public AjaxResult wxLogin(@RequestBody WxLoginBody wxLoginBody)
    {
        logger.info("登录参数:" + JSON.toJSONString(wxLoginBody));
        //获取登录凭证 只能用一次
        String code = wxLoginBody.getCode();
        //秘钥
        String encryptedIv = wxLoginBody.getEncryptedIv();
        //加密数据
        String encryptedData = wxLoginBody.getEncryptedData();

        //想微信服务器发送请求获取用户信息
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppConfig.getAppId() + "&secret=" + wxAppConfig.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
        String res = restTemplate.getForObject(url, String.class);
        JSONObject jsonObject = JSONObject.parseObject(res);

        //获取session_key和openid
        String sessionKey = jsonObject.getString("session_key");
        String openid = jsonObject.getString("openid");

        //解密
        String decryptResult = "";
        try {
            //如果没有绑定微信开放平台,解析结果是没有unionid的。
            decryptResult = decrypt(sessionKey,encryptedIv,encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("微信登录失败!");
        }

        if (StringUtils.hasText(decryptResult)){
            //如果解析成功,获取token
            String token = loginService.wxLogin(decryptResult);
            AjaxResult ajax = AjaxResult.success();
            ajax.put(Constants.TOKEN, token);
            return ajax;
        }else{
            return AjaxResult.error("微信登录失败!");
        }
    }

    /**
     * AES解密
     * @param sessionKey
     * @param encryptedIv
     * @param encryptedData
     */
    private String decrypt(String sessionKey,String encryptedIv,String encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        //转化为字节数组
        byte[] key = Base64.decode(sessionKey);
        byte[] iv = Base64.decode(encryptedIv);
        byte[] encData = Base64.decode(encryptedData);
        // 如果密钥不足16位,那么就补足
        int base = 16;
        if (key.length % base != 0) {
            int groups = key.length / base + (key.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(key, 0, temp, 0, key.length);
            key = temp;
        }

        // 如果初始向量不足16位,也补足
        if (iv.length % base != 0) {
            int groups = iv.length / base + (iv.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(iv, 0, temp, 0, iv.length);
            iv = temp;
        }

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        String resultStr = null;

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            resultStr = new String(cipher.doFinal(encData), "UTF-8");
        } catch (Exception e) {
            logger.info("解析错误!");
            e.printStackTrace();
        }

        //解析解密后的字符串
        return resultStr;
    }
}

3.10 SysLoginService新增wxLogin方法

/**
     * 微信登录
     *
     * 备注:本来想用unionid作为唯一标识的,但是,小程序需要绑定微信开放平台,并且需要500大洋,我没钱,所以,就用openid作为唯一标识了。
     * @param decryptResult 登录凭证 只能用一次
     * @return
     */
    public String wxLogin(String decryptResult){
        //字符串转json
        JSONObject jsonObject = JSONObject.parseObject(decryptResult);
        //获取openid,这里本可以获取unionid的,可惜没钱
//        String unionid = jsonObject.getString("unionid");
        String openId = jsonObject.getString("openId");
        //获取nickName
        String nickName = jsonObject.getString("nickName");
        //获取头像
        String avatarUrl = jsonObject.getString("avatarUrl");
        //还可以获取其他信息
        //根据openid判断数据库中是否有该用户
        //根据openid查询用户信息
        SysUser wxUser = userMapper.selectWxUserByOpenId(openId);

        //如果查不到,则新增,查到了,则更新
        SysUser user = new SysUser();
        if (wxUser == null) {
            // 新增
            // 设置姓名 ,默认使用昵称+随机数,防止重复姓名的发生 数据库中把username的长度修改的长一点
            user.setUserName(IdUtils.fastSimpleUUID());
            user.setNickName(nickName);
            //数据库中把长度修改的长一点
            user.setAvatar(avatarUrl);
            // 如果有unionid,则设置unionid
            // wxUser.setUnionId(unionid);
            user.setOpenId(openId);
            user.setCreateTime(DateUtils.getNowDate());
            //新增 用户
            userMapper.insertUser(user);
        }else {
            //更新
            user = wxUser;
            user.setNickName(nickName);
            user.setAvatar(avatarUrl);
            user.setUpdateTime(DateUtils.getNowDate());
            userMapper.updateUser(user);
        }

        //组装token信息
        LoginUser loginUser = new LoginUser();
        loginUser.setOpenId(openId);
        //如果有的话设置
//        loginUser.setUnionId(unionid);
        loginUser.setUser(user);
        loginUser.setUserId(user.getUserId());

        // 生成token
        return tokenService.createToken(loginUser);
    }

3.11 SecurityConfig中白名单加入"/wxLogin"

// 对于登录login 注册register 验证码captchaImage 允许匿名访问
  .antMatchers("/login","/wxLogin", "/register", "/captchaImage").permitAll()

3.12 修改SysPermissionService下的getMenuPermission方法

这里的判断采用!CollectionUtils.isEmpty(roles) ,替换原有的!roles.isEmpty()

if (!CollectionUtils.isEmpty(roles) && roles.size() > 1)
            {
                // 多角色设置permissions属性,以便数据权限匹配权限
                for (SysRole role : roles)
                {
                    Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
                    role.setPermissions(rolePerms);
                    perms.addAll(rolePerms);
                }
            }

下载源码

扫码关注公众号【立志成为好青年】回复 ruoyi 即可获取下载链接
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值