若依框架 + uniApp 实现微信小程序授权登录

1.1 环境准备
1.2 登陆流程

        

 2.1 创建uniapp项目,并开启OAuth登陆鉴权微信登陆

        双击manifest.json进行设置

             

2.2 增加微信登陆授权按钮
<button type="default" @click="wxLogin">微信登陆</button>
2.3 创建wxLogin()方法
export default {
		data() {
			return {
                /**微信登录的form数据**/
				wxLoginForm: {
					code: "",
					encryptedIv: "",
					encryptedData: "",
				}
            }
        },
        methods: {
            /**微信授权登录**/
            	async wxhandleLogin() {
				uni.getProvider({
					service: 'oauth',
					success: (res) => {
						if (~res.provider.indexOf("weixin")) {
							uni.login({
								provider: "weixin",
								success: (loginRes) => {
									this.wxLoginForm.code = loginRes.code
									uni.getUserInfo({
										success: (infoRes) => {
											this.wxLoginForm.encryptedIv = infoRes.iv						            this.wxLoginForm.encryptedData=infoRes.encryptedData
											this.sendWxLoginFormToLocalService()
										}
									})
								}
							})
						}
					}
				})
			},
            sendWxLoginFormToLocalService() {
				this.$store.dispatch('WxLogin', this.wxLoginForm).then(() => {
					this.$modal.closeLoading()
					this.loginSuccess()
					console.log('登录成功')
				}).catch(() => {
					console.log('登录失败')
				})
			},
        }
}
3.1 application.yml配置文件中,增加微信小程序appId和appSecret

        ruoyi-admin/src/main/resources/application.yml

wx-app:
  appId: ************************
  appSecret: ****************************
3.2 在数据库sys_user表中,增加open_id和union_id字段
OpenID: 普通用户的标识,对当前开发者帐号唯一。一个openid对应一个公众号或小程序;
UnionID: 用户统一标识,针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。

        本例中,我们只用到open_id

        根据业务需要,如多个应用可接入统一标识UnionID。

3.3 在SysUser类中 新增 两个属性

        ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

/** 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;
}

@Override
public String toString() {
    return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
        .append("userId", getUserId())
        .append("deptId", getDeptId())
}
3.4 SysUserMapper 新增 selectWxUserByOpenId

        ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

/**
* 根据openId查询用户信息
* @param openId
* @return
*/
public SysUser selectWxUserByOpenId(String openId);
3.5 SysUserMapper.xml改造

        ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

        resultMap 增加 openId的字段映射

<resultMap type="SysUser" id="SysUserResult">
    <id     property="userId"       column="user_id"      />
    <result property="deptId"       column="dept_id"      />
    <result property="userName"     column="user_name"    />
    <result property="nickName"     column="nick_name"    />
    <result property="email"        column="email"        />
    <result property="phonenumber"  column="phonenumber"  />
    <result property="sex"          column="sex"          />
    <result property="avatar"       column="avatar"       />
    <result property="password"     column="password"     />
    <result property="status"       column="status"       />
    <result property="delFlag"      column="del_flag"     />
    <result property="loginIp"      column="login_ip"     />
    <result property="loginDate"    column="login_date"   />
    <result property="createBy"     column="create_by"    />
    <result property="createTime"   column="create_time"  />
    <result property="updateBy"     column="update_by"    />
    <result property="updateTime"   column="update_time"  />
    <result property="openId"       column="open_id"  />
    <result property="remark"       column="remark"       />
    <association property="dept"    column="dept_id" javaType="SysDept" resultMap="deptResult" />
    <collection  property="roles"   javaType="java.util.List"           resultMap="RoleResult" />
</resultMap>

<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
    select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time,u.open_id, u.remark, d.dept_name, d.leader from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    where u.del_flag = '0'
    <if test="userId != null and userId != 0">
        AND u.user_id = #{userId}
    </if>
    <if test="userName != null and userName != ''">
        AND u.user_name like concat('%', #{userName}, '%')
    </if>
    <if test="status != null and status != ''">
        AND u.status = #{status}
    </if>
    <if test="phonenumber != null and phonenumber != ''">
        AND u.phonenumber like concat('%', #{phonenumber}, '%')
    </if>
    <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
        AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
    </if>
    <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
        AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
    </if>
    <if test="deptId != null and deptId != 0">
        AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
    </if>
    ${params.dataScope}
</select>
<select id="selectWxUserByOpenId" parameterType="String" resultMap="SysUserResult">
    <include refid="selectUserVo" />
    where u.open_id = #{openId} and u.del_flag = '0'
</select>
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
    insert into sys_user(
    <if test="userId != null and userId != 0">user_id,</if>
    <if test="deptId != null and deptId != 0">dept_id,</if>
    <if test="userName != null and userName != ''">user_name,</if>
    <if test="nickName != null and nickName != ''">nick_name,</if>
    <if test="email != null and email != ''">email,</if>
    <if test="avatar != null and avatar != ''">avatar,</if>
    <if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
    <if test="sex != null and sex != ''">sex,</if>
    <if test="password != null and password != ''">password,</if>
    <if test="status != null and status != ''">status,</if>
    <if test="createBy != null and createBy != ''">create_by,</if>
    <if test="openId != null and openId != ''">open_id,</if>
    <if test="remark != null and remark != ''">remark,</if>
    create_time
    )values(
    <if test="userId != null and userId != ''">#{userId},</if>
    <if test="deptId != null and deptId != ''">#{deptId},</if>
    <if test="userName != null and userName != ''">#{userName},</if>
    <if test="nickName != null and nickName != ''">#{nickName},</if>
    <if test="email != null and email != ''">#{email},</if>
    <if test="avatar != null and avatar != ''">#{avatar},</if>
    <if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
    <if test="sex != null and sex != ''">#{sex},</if>
    <if test="password != null and password != ''">#{password},</if>
    <if test="status != null and status != ''">#{status},</if>
    <if test="createBy != null and createBy != ''">#{createBy},</if>
    <if test="openId != null and openId != ''">#{openId},</if>
    <if test="remark != null and remark != ''">#{remark},</if>
    sysdate()
    )
</insert>
3.6 ApplicationConfig增加 HTTP 请求工具配置

        ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java

/**
* 远程调用  HTTP 请求工具
*/
@Bean
public RestTemplate RestTemplate(){
    return new RestTemplate();
}
3.7 新增WxLoginBody类

        ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/WxLoginBody.java

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

/**
 * 微信用户登录对象
 *
 * @author ruoyi
 */
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;
    }
}
3.8 SysLoginController,新增接口wxLogin和AES解密方法

        ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@Autowired
private WxAppConfig wxAppConfig;

@PostMapping("/wxLogin")
public AjaxResult wxLogin(@RequestBody WxLoginBody 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解密
     */
private String decrypt(String sessionKey,String encryptedIv,String encryptedData) throws Exception{
    // 转化为字节数组
    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.9 SecurityConfig白名单放行Wxlogin接口

        ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

// 对于登录login 注册register 验证码captchaImage 微信登陆wxLogin 允许匿名访问
.antMatchers("/login","/wxLogin", "/register", "/captchaImage").permitAll()
3.9.1 SysLoginService新增wxLogin方法

        ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java

/**
     * 微信登录
     *
     * @param decryptResult 登录凭证 只能用一次
     * @return
     */
public String wxLogin(String decryptResult){
    //字符串转json
    JSONObject jsonObject = JSONObject.parseObject(decryptResult);
    //        String unionid = jsonObject.getString("unionid");
    String openId = jsonObject.getString("openId");
    System.out.println("openId"+openId);
    //获取nickName
    String nickName = getStringRandom(16);// 生成16位随机昵称
    //获取头像
    //        String avatarUrl = jsonObject.getString("avatarUrl");
    String avatarUrl = "";
    //还可以获取其他信息
    //根据openid判断数据库中是否有该用户
    //根据openid查询用户信息
    SysUser wxUser = userMapper.selectWxUserByOpenId(openId);

    //如果查不到,则新增,查到了,则更新
    SysUser user = new SysUser();
    if (wxUser == null) {
        // 新增
        user.setUserName(getStringRandom(16));// 生成16位随机用户名
        user.setNickName(nickName);
        user.setAvatar(avatarUrl);
        //            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.setUser(user);
    loginUser.setUserId(user.getUserId());

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

//生成随机用户名,数字和字母组成,
public static String getStringRandom(int length) {

    String val = "";
    Random random = new Random();

    //参数length,表示生成几位随机数
    for (int i = 0; i < length; i++) {

        String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
        //输出字母还是数字
        if ("char".equalsIgnoreCase(charOrNum)) {
            //输出是大写字母还是小写字母
            int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
            val += (char) (random.nextInt(26) + temp);
        } else if ("num".equalsIgnoreCase(charOrNum)) {
            val += String.valueOf(random.nextInt(10));
        }
    }
    return val;
}
4 演示效果

点击微信登陆后,向wxLogin()发起请求,如果系统用户中有open_id,则返回token;如如该用户open_id,则增加该用户,用户名与昵称随机生成16位字符,调用登陆接口,返回token。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值