小程序登录

目录

一、官网登录时序图

二、实现方式

三、代码如下:


一、官网登录时序图

二、实现方式

1、换取(session_key,openId):将小程序的code值返回给后端,获取session_key,openId,存入redis中,返回一个sessinId给前端。

2、登录:通过前端传回来的sessionID,去redis中获取(session_kye)并和 encryptedData,iv ,这三个参数,获取据。自定义用户登录状态(通过openID进行关联)

三、代码如下:

controller层:

@RestController
@RequestMapping("/miniprogram/")
@Api(tags = {"小程序登录管理"}, description = "小程序登录")
@Slf4j
public class UserController {

   @Resource
   private MiniProgramService miniProgramService;

   @GetMapping(value = "/getSessionId")
   @ApiOperation(value = "获取sessinID-通过前端传递过来的code")
   public Result<?> getSessionId(String code){
      String sessionId = miniProgramService.getSessionId(code);
      if(StringUtils.isNotBlank(sessionId)){
         return Result.OK(sessionId);
      }
      return Result.error("获取sessionId失败");
   }


   @PostMapping(value = "/autoLogin")
   @ApiOperation(value = "autoLogin-用户进行登录")
   public Result<?> autoLogin(@RequestBody WXAuth wxAuth){
      try {
         if(miniProgramService.autoLogin(wxAuth)){
            return Result.OK("用户登录成功");
         }else {
            return Result.error("用户登录失败");
         }
      } catch (Exception e) {
         log.info("用户登录失败,原因是:"+e.getMessage());
         return Result.error("用户登录失败");
      }
   }
 
}

service层

public interface MiniProgramService extends IService<WXUser> {

    /**
     *
     * @param code  前端传入的code值
     * @return 返回sessinid
     */
    String getSessionId(String code);

    /**
     *
     * @param wxAuth  前端传入的参数三个
     * @return
     * @throws Exception
     */
    boolean  autoLogin(WXAuth wxAuth);

    /**
     *
     * @param token
     * @param refresh
     * @return
     */
    boolean getUserInfo(String token, boolean refresh);

}
WXService (发请求)
@Service
@Slf4j
public class WXService {
    @Resource
    private RestTemplate restTemplate;



    /**
     * 获取Headers
     * @return HttpHeaders 头部信息
     */
    public HttpHeaders getHeaders(int type) {
        HttpHeaders headers = new HttpHeaders();
        MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(mediaType);
        //0为token 1为其他
        if (CommonConstant.DATA_INT_0 == type) {
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        }
        headers.add("accept", "application/json");
        return headers;
    }



    /**
     * 发送数据
     * @param url 请求接口地址
     * @param headers 请求头
     * @param t 请求体参数
     * @param type  请求方式
     * @return 返回值
     */
    public String postHeadersT(String url, HttpHeaders headers, Object t, HttpMethod type) {
        try {
            log.info("restTemplate 接口,url:" + url);
            HttpEntity<Object> entity;
            if (t != null) {
                entity = new HttpEntity<>(t, headers);
            } else {
                entity = new HttpEntity<>(headers);
            }
            ResponseEntity<String> responseEntity;
            //将请求头部和参数合成一个请求
            responseEntity = restTemplate.exchange(url, type, entity, String.class);
            //执行HTTP请求,将返回的结构使用ResultVO类格式化
            String resultData = responseEntity.getBody();
            log.info("返回数据:" + JSON.toJSONString(resultData));
//            if (StringUtils.isNotBlank(resultData)) {
//                JSONObject parseObject = JSONObject.parseObject(resultData);
//                if (parseObject != null && !parseObject.isEmpty()) {
//                    if (CommonConstant.DATA_STR_0.equals(parseObject.get(CommonConstant.REST_TEMPLATE_RESULT_CODE).toString())
//                            || CommonConstant.DATA_STR_200.equals(parseObject.get(CommonConstant.REST_TEMPLATE_RESULT_CODE).toString())){
//                        return resultData;
//                    }
//                }
//            }
            return resultData;
        } catch (Exception e) {
            log.error("发送数据异常,url为" + url + "异常原因为:" + e);
            return null;
        }
    }
}
WXServiceExt extends WXService
@Service
@Slf4j
public class WXServiceExt extends WXService {

    @Value("${wx.appid}")
     String appid;
    @Value("${wx.secret}")
     String secret;

    public JSONObject sendWX(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code";
        String resultData = postHeadersT(url, getHeaders(CommonConstant.DATA_INT_1), null, HttpMethod.GET);
        JSONObject parseObject = JSONObject.parseObject(resultData);
        return parseObject;
    }
}

serviceImpl层

@Slf4j
@Service
public class MiniProgramServiceImpl extends ServiceImpl<MiniUserMapper, WXUser> implements MiniProgramService {
    @Resource
    WXServiceExt wxServiceExt;
    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    MiniProgramService miniProgramService;

    @Autowired
    private JWTUtil jwtUtil;


    @Override
    public String getSessionId(String code) {
        try {
            JSONObject jsonObject = wxServiceExt.sendWX(code);
            String s = jsonObject.toString();
            if(s.contains(CommonConstant.ERRMSG)){
                throw new UnsupportedOperationException("当前code 值"+code + "已经使用,原因是:"+s);
            }
            String uuid = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(CommonConstant.WX_SESSION_ID + uuid, jsonObject, 10, TimeUnit.MINUTES);
            return uuid;
        } catch (Exception e) {
            log.info("获取session_key,openid失败,原因是:" + e.getMessage());
            return "";
        }
    }

    //使用jwt技术生产要给token,提供给前端 token 令牌,用户在下一次登录的时候,携带token访问
    //后端进行验证,token 知道是哪个用户登录u的
    @Override
    public boolean autoLogin(WXAuth wxAuth) {
        try {
            String json = redisTemplate.opsForValue().get(CommonConstant.WX_SESSION_ID + wxAuth.getSessionId()).toString();
            //获取redis中的session_key
            String sessionKey = JSON.parseObject(json).get(CommonConstant.SESSION_KEY).toString();
            String openId = JSON.parseObject(json).get(CommonConstant.OPPENID).toString();
            //解析加密数据
            String userInfo = WXUtils.wxDecrypt(wxAuth.getEncryptedData(), sessionKey, wxAuth.getIv());
            WXUser wxUser = JSON.parseObject(userInfo, WXUser.class);
            WXUser user = miniProgramService.getOne(new QueryWrapper<WXUser>().eq(CommonConstant.OPPEN_ID, openId).last("limit 1"));
            if (!Objects.isNull(user)) {
                return this.login(user);
            } else {
                //需要注册
                return this.register(wxUser,openId);
            }
        } catch (Exception e) {
            log.info("用户登录/注册失败,原因是:"+e.getMessage());
            return false;
        }
    }

    private boolean register(WXUser wxUser,String openId) {
        try {
            WXUser user = miniProgramService.getOne(new QueryWrapper<WXUser>().eq(CommonConstant.OPPEN_ID, openId)
                    .eq(CommonConstant.IDEL,CommonConstant.DATA_INT_0).last("limit 1"));
            if(Objects.isNull(user)){
                wxUser.setOpenId(openId);
                wxUser.setIdel(CommonConstant.DATA_INT_0);
                miniProgramService.save(wxUser);
            }
            return this.login(wxUser);
        } catch (Exception e) {
            log.info("用户注册失败,原因是"+e.getMessage());
            return false;
        }
    }


    private boolean login(WXUser wxUser) {
        try {
            String token = jwtUtil.creatJwtToken(wxUser.getId());
            wxUser.setToken(token);
            miniProgramService.saveOrUpdate(wxUser);
            //保存到redis内,下次就直接跳过验证
            redisTemplate.opsForValue().set(CommonConstant.TOKEN + token, JSON.toJSONString(wxUser), 7, TimeUnit.DAYS);
            return true;
        } catch (Exception e) {
            log.info("用户登录失败,原因是"+e.getMessage());
            return false;
        }
    }

    @Override
    public boolean getUserInfo(String token, boolean refresh) {

        /**
         * 1 验证token是否有效
         * 拿到登录时候的redis 的键,redis.token+token   (实体类),
         * 获取user, 如果拿到的值是空,也是未登录的状态
         *
         * 如果refresh 是true,重新生产token,签发,存入redis
         * 2 refresh true 代表刷新, 重新生成tokne和redis里 重新存储
         * 3 false  直接返回用户信息,redis 中查询出来,直接返回
         */

        return false;
    }




}

entity层

-----------解析参数
@Data
public class WXAuth implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "后台返回给前端的sessionId")
    private String sessionId;
    @ApiModelProperty(value = "加密数据")
    private String encryptedData;
    @ApiModelProperty(value = "偏移量")
    private String iv;
}



数据库对应实体
@Data
@TableName("wx_user")
public class WXUser implements Serializable {
    private static final long serialVersionUID = 1L;
    private java.lang.String id;

    private java.lang.String token;

    private java.lang.String nickName;

    private java.lang.String avatarUrl;

    private java.lang.Integer gender;

    private java.lang.String country;

    private java.lang.String province;

    private java.lang.String city;

    private java.lang.String mobile;

    private java.lang.String openId;

    private java.lang.String unionId;

    private java.lang.Integer idel;

    private java.lang.String password;

    private java.lang.String userName;

}







public interface CommonConstant {

    /**
     * 微信登录
     *
     */
    String WX_SESSION_ID="wx_session_id_";
    String SESSION_KEY="session_key";
    String DATA_STR_200 = "200";
    String NODE_SUCCESS= "success";
    String REST_TEMPLATE_RESULT_CODE= "code";
    String OPPEN_ID= "open_id";
    String OPPENID= "openid";
    Integer DATA_INT_1 = 1;
    Integer DATA_INT_0 = 0;
    String DATA_STR_0 = "0";
    String TOKEN = "token_";
    String ERRMSG = "errmsg";
    String IDEL = "idel";
}

wxUtils(解密)

public class WXUtils {


    public static String wxDecrypt(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);

        try {
            // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
//                return JSONObject.parseObject(result);
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }


//    public static String getUserInfo(String encryptedData, String sessionKey, String iv) throws Exception{
//        //开始解密
//        //String json redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID sessionId);
//        //JSONObject jsonobject = JSON.parseobject(json);
//
//       // String sessionKey (String) jsonobject.get("session_key");
//        byte[] encData = cn.hutool.core.codec.Base64.decode(encryptedData);
//        byte[] vi = cn.hutool.core.codec.Base64.decode(iv);
//        byte[] key = Base64.decode(sessionKey);
//        AlgorithmParameterSpec ivSpec =  new IvParameterSpec(vi);
//        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
//        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
//        return new String(cipher.doFinal(encData), "UTF-8");
//    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值