首页 > 其他分享 >微信授权登录接口开发

微信授权登录接口开发

时间:2024-09-17 17:23:37浏览次数:1  
标签:String 登录 微信 接口 token 注解 public

微信登陆过程

在项目开发中,难免会遇到微信授权登录这一操作,本讲来讲一下微信登陆是如何实现的?

关于校验登录,有诸多方法,记录方法如下:

  • 使用Spring MVC提供的拦截器
  • 网关服务全局过滤器
  • 使用AOP面向横切面实现

对于使用Spring MVC提供的拦截器来实现,其大致的思路如下:

注意:

  1. 用户登录成功以后,会生成对应的token,并且会将登录用户的信息存储到Redis中(键:token 值:userInfo),然后将token返回给前端
  2. 需要在每一个业务微服务中定义拦截器,影响开发效率,代码维护性较低。(解决方案:可以将拦截器定义到公共的模块中)

对于使用网关服务的全局过滤器来完成校验登录, 需要开发全局过滤器(GlobalFilter)

对于使用AOP横切面完成统一登录校验逻辑,配合自定义注解,哪一个业务方法需要验证用户登录状态,就在该方法上添加自定义注解。

这里呢我们推荐使用第三种,因此对于有的服务来讲,是不需要客户端进行登陆操作就可以直接访问的,其自由度不高。我们可以使用自定义注解来完成登录的校验,这样对于有需要登录信息的接口我们只需要加上我们自定义的注解即可,对于不需要登录信息的接口就可以不用加自定义注解。

1. AOP实现登录校验步骤

1.1 自定义登陆注解

首先自定义一个注解,该注解为登陆注解。

注解作用:哪些需要登录才能访问必须要添加,那些需要获取到用户Id 也必须加这个注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Login {
    boolean required() default true;		// 是否必须要登录    
}

1.2 自定义切面类

@Aspect
@Component
public class LoginAspect {

    @Autowired
    private RedisTemplate<String , String> redisTemplate;

    @SneakyThrows
    @Around("execution(* com.*.*.api.*.*(..)) && @annotation(Login)")	// 匹配方法上带有@Login注解的方法对其实现功能增强
    public Object loginAspect(ProceedingJoinPoint point, Login login){

        //  获取HttpServletRequest对象
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = sra.getRequest();
        String token = request.getHeader("token");

        //  判断是否需要登录
        if (login.required()){

            //  必须要登录,token 为空是抛出异常
            if (StringUtils.isEmpty(token)){
                throw new Exception(ResultCodeEnum.LOGIN_AUTH);   //  没有token 要抛出异常
            }

            //  如果token 不为空,从缓存中获取信息.
            String userInfoJSON =  this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);

            //  判断对象是否为空
            if (StringUtils.isEmpty(userInfoJSON)){
                throw new Exception(ResultCodeEnum.LOGIN_AUTH);            //  抛出异常信息
            }

            // 将用户信息存储到ThreadLocal中
            // AuthContextHolder 底层就是 ThreadLocal
            UserInfo userInfo = JSON.parseObject(userInfoJSON, UserInfo.class);
            AuthContextHolder.setUserId(userInfo.getId());
            AuthContextHolder.setUsername(userInfo.getNickname());

        }else {

            //  不需要强制登录,但是,有可能需用到用户信息.
            if (!StringUtils.isEmpty(token)){

                //  如果token 不为空,从缓存中获取信息.
                String userInfoJSON =  this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
                if (!StringUtils.isEmpty(userInfoJSON)){

                    //  将用户信息存储到ThreadLocal中
                    UserInfo userInfo = JSON.parseObject(userInfoJSON, UserInfo.class);
                    AuthContextHolder.setUserId(userInfo.getId());
                    AuthContextHolder.setUsername(userInfo.getNickname());

                }

            }

        }

       try {
            // 执行目标方法并获取目标方法执行后的返回结果
            Object proceed = point.proceed();

            // 返回
            return proceed ;

        }catch (Throwable e) {
            e.printStackTrace();
        } finally {
            // 从ThreadLocal中删除数据,防止内存泄漏和移除
            AuthContextHolder.removeUserId();
            AuthContextHolder.removeUsername();
        }

        return null ;
    }
}

AuthContextHolder类:

/**
 * 获取当前用户信息帮助类
 */
public class AuthContextHolder {

    private static ThreadLocal<Long> userId = new ThreadLocal<Long>();
    private static ThreadLocal<String> username = new ThreadLocal<String>();

    public static void setUserId(Long _userId) {
        userId.set(_userId);
    }

    public static Long getUserId() {
        return userId.get();
        // return 1L;
    }

    public static void removeUserId() {
        userId.remove();
    }

    public static void setUsername(String _username) {
        username.set(_username);
    }

    public static String getUsername() {
        return username.get();
    }

    public static void removeUsername() {
        username.remove();
    }
}

1.3 微信登陆流程介绍

参数说明:

1、前端调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。

2、后端调用 auth.code2Session 接口,换取用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账

号)和 会话密钥 session_key

3、之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

注意事项:

1、会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个

密钥

2、临时登录凭证 code 只能使用一次

配置WxMaService

WechatAccountConfig

定义一个实体类读取service-user-dev.yaml配置文件中关于微信开放平台的相关配置。

@Data
@ConfigurationProperties(prefix = "wechat.login")
public class WechatAccountConfig {
    private String appId;					//  公众平台的appdId
    private String appSecret;				//  小程序微信公众平台秘钥
}

// 启动类添加如下注解
@EnableConfigurationProperties(value = WechatAccountConfig.class)

配置文件中定义了微信授权登录的appId和appSecret

wechat:
  login:
    #小程序授权登录
    appId:   # 小程序微信公众平台appId
    appSecret:   # 小程序微信公众平台api秘钥

WeChatMpConfig

WxMaService配置类

@Configuration
public class WeChatMpConfig {

    @Autowired
    private WechatAccountConfig wechatAccountConfig;

    @Bean
    public WxMaService wxMaService(){
        WxMaDefaultConfigImpl wxMaConfig =  new WxMaDefaultConfigImpl();		        //  创建对象
        wxMaConfig.setAppid(wechatAccountConfig.getAppId());
        wxMaConfig.setSecret(wechatAccountConfig.getAppSecret());
        wxMaConfig.setMsgDataFormat("JSON");
        WxMaService service = new WxMaServiceImpl();		 //  创建 WxMaService 对象
        service.setWxMaConfig(wxMaConfig);					  //  给 WxMaService 设置配置选项
        return service;
    }
   
}

登录接口开发

@Operation(summary = "小程序授权登录")
@GetMapping("/wxLogin/{code}")
public Result<Map<String , String>> wxLogin(@PathVariable String code) throws WxErrorException {
    Map<String , String> tokenInfo = wxLoginService.wxLogin(code) ;
    return Result.build(tokenInfo , ResultCodeEnum.SUCCESS) ;
}

获取登录用户的openId信息

public void wxLogin(String code) throws WxErrorException {

    //  获取从微信服务端返回的结果
    WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);

    // 获取openId
    String openId = sessionInfo.getOpenid();
    UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openId));

    //  创建token
    String token = UUID.randomUUID().toString().replaceAll("-","");
    
    // 进行后续操作...从存入Redis中等业务
}

标签:String,登录,微信,接口,token,注解,public
From: https://www.cnblogs.com/lilyflower/p/18417331

相关文章

  • go使用泛型实现接口通用结构体转换
    packagepagemodelimport("gitee.com/leijmdas/gobase/goconfig/common/base/basedto""gitee.com/leijmdas/gobase/goconfig/common/base/baseiface""gitee.com/leijmdas/gobase/goconfig/common/base/jsonutils""......
  • 毕业设计选题参考|基于微信小程序实现养老院管理系统
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企业......
  • 第十一章 抽象类与接口
    一、抽象类和抽象方法抽象类:使用abstract修饰的类抽象方法:在类中没有方法体的方法,称为抽象方法,抽象方法用abstract修饰抽象类中可以没有抽象方法,包含抽象方法的类必是抽象类如果子类没有实现父类中的全部抽象方法,子类也必是抽象类没有抽象构造方法,抽象类不能被实例化抽象......
  • C++11 线程同步接口std::condition_variable和std::future的简单使用sk
    合集-C++(1)1.C++11线程同步接口std::condition_variable和std::future的简单使用09-17收起std::condition_variable条件变量std::condition_variable有wait和notify接口用于线程间的同步。如下图所示,Thread2阻塞在wait接口,Thread1通过notify接口通知Thread2继续执行。......
  • 基于通信协议与技术架构的API接口分类探讨
    API的全称是“ApplicationProgrammingInterface”,意为“应用程序编程接口”API接口的分类:按照技术分WebAPI:基于Web的API,通过HTTP和HTTPS协议与应用程序交互,如RESTfulAPI和SOAPAPI。云API:用于在云计算环境中管理和操作云服务,例如AmazonWebServicesAPI和MicrosoftA......
  • python+flask计算机毕业设计基于微信小程序的综合旅游管理系统的设计与实现(程序+开题+
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着移动互联网技术的飞速发展,人们的生活方式和消费习惯正经历着深刻的变革。旅游作为现代人休闲娱乐的重要方式之一,其服务模式和体验需求......
  • 文本多语言 AI 摘要 API 数据接口
    文本多语言AI摘要API数据接口文本/文本摘要AI生成文本摘要AI处理/智能摘要。1.产品功能支持多语言摘要生成;支持长文本处理;基于AI模型,持续迭代优化;不存储PDF文件,处理完即释放,保证您的文档安全;全接口支持HTTPS(TLSv1.0/v1.1/v1.2/v1.3);全面兼容Ap......
  • C++11 线程同步接口std::condition_variable和std::future的简单使用
    std::condition_variable条件变量std::condition_variable有wait和notify接口用于线程间的同步。如下图所示,Thread2阻塞在wait接口,Thread1通过notify接口通知Thread2继续执行。具体参见示例代码:#include<iostream>#include<mutex>#include<thread>#include<queue>std......
  • 不服不行,这才是后端API接口应该有的样子!
    在移动互联网,分布式、微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式,(题外话:前后端的工作职责越来越明确,现在的前端都称之为大前端,技术栈以及生态圈都已经非常成熟;以前后端人员瞧不起前端人员,那现在后端人员要重新认识一下前端,前端已经很成体系了)。一般系统的大......
  • 微信小程序开发中的客户端与服务端交互
    微信小程序开发中的客户端与服务端交互1.搭建桥梁:客户端与服务端的握手初次见面:理解客户端与服务端的角色握手协议:HTTP与HTTPS的基本通信原理桥梁建设:使用wx.request发起网络请求2.数据的往返:构建高效的数据传输通道轻装简行:简化数据格式提高传输效率JSON之舞:JSON数......