2022最新微信小程序授权登录(前后端分离)

一、前言

首先说一下微信小程序最近两个比较大的变动:

1. 获取用户信息接口由原来的wx.getUserInfo更换为wx.getUserProfile

2021年4月28日24时后发布的新版本小程序,开发者调用wx.getUserInfo将不再弹出弹窗,直接返回匿名的用户个人信息,获取加密后的openID、unionID数据的能力不做调整。

新增getUserProfile接口,若开发者需要获取用户的个人信息,可以通过wx.getUserProfile接口进行获取,该接口只返回用户个人信息,不包含用户身份标识符。该接口中desc属性(声明获取用户个人信息后的用途)后续会展示在弹窗中,请开发者谨慎填写。开发者每次通过该接口获取用户个人信息均需用户确认,请开发者妥善保管用户快速填写的头像昵称,避免重复弹窗。

新版本的小程序最直观的感受:进入小程序时不会立刻跳出弹窗,而是当用户进行相关操作,比如点击了某个请求按钮,才会跳出弹窗提示授权。

官方公告:小程序登录、用户信息相关接口调整说明 | 微信开放社区

2. 小程序获取用户信息相关接口,不再返回用户性别及地区信息

根据相关法律法规,进一步规范开发者调用用户信息相关接口,小程序获取用户信息相关接口,不再返回用户性别及地区信息,这也就意味着,现在开放的接口只能获取到用户的头像和昵称两个用户信息,其余信息需要用户自己填写。

对开发者而言,这次的改动降低了获取信息的难度,但相对的,获取到的数据的重要性也下降了。以往获取信息的方式需要小程序端获取encryData、iv到后端进行解密,后端再返回给前端相关信息,而现在可以直接获取头像与用户名,只需调用后端接口将其存储到数据库即可。

官方公告:微信公众平台用户信息相关接口调整公告 | 微信开放社区

二、前置准备

1. 技术栈

前端:微信小程序开发(不使用云开发)

后端:spring boot + mysql + mybatis + jwt

2. 了解登录流程

大致流程:

1. 前端调用wx.login获取code,再调用后端接口传递code

注意:code是临时的,只有5分钟的使用时间,而且只能使用一次

2. 后端用获取的code与微信接口服务换取openid(用户唯一标识)与session_key(可以用于解密私密信息encrydata,现在只能获取头像和昵称),关联openid和session_key自定义登录态session,利用session生成token

注意:不可以把解析出来的openid和session_key直接返回给前端,会造成信息安全问题

3. 将token返回给前端

4. 前端缓存token

5. 用户登录时,登录接口获取到token,再调用其他接口时,拦截器进行拦截,如果token有效,则放行请求;如果token失效(不存在、过期、格式不正确等原因),则无法访问该接口,需要重新登录。

说明:如果觉得token验证太过复杂,也可以退而求其次,采用微信小程序自带的wx.checkSeesion检查下发的session_key是否过期(固定为两天)。

wx.checkSeesion是前端检查,非常方便,但是缺点也很明显:耗时长,通常需要300+ms ,另外前后端传递私密数据时,需要额外考虑数据安全问题(以openid为例,前端每次需要传递openid时,都需要先获取临时code,再传递给后端,后端再用code换取openid,开销极大),因此正式开发时极不建议使用wx.checkSeesion,token验证方式可以较好解决上述问题。

三、开发代码

1、后端代码

1. config包(主要是一些配置信息)

1.InterceptorConfig类(拦截器配置类)

这里有一点需要注意,拦截器加载的时间点在springcontext之前,会导致拦截器中自动注入为null,因此需要用@Bean提前加载;

另外,addPathPatterns用于添加拦截的路径,理论上除了登入登出接口,其他接口都需要拦截。

为什么要使用拦截器?因为前端获取到token后,如果每次请求都在请求体中加入token,会导致前后端代码非常冗长,因此可以将token放置于请求头header中,每次请求利用拦截器进行拦截,开发者仅需关注业务逻辑信息。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Bean
    public JwtInterceptor getJwtInterceptor(){
        return new JwtInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(getJwtInterceptor())
                .addPathPatterns("/user/**") //拦截用户接口
                .excludePathPatterns("/user/index/**");//登录接口不拦截
    }

}

2、common(公共包)

与util包有一定区别,util包一般放置静态工具类,当工具类较多时应该使用common包进行细化

1. Result类(用于返回消息,简化版,实际状态码远不止两个)

@Data
@NoArgsConstructor
public class Result {
    private int code;
    private String msg;
    private Object data;

    public static Result succ(Object data){
        return succ(200,"操作成功",data);
    }

    public static Result succ(int code, String msg, Object data) {
        Result r = new Result();
        r.setCode(code);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }

    public static Result succ(String msg, Object data) {
        Result r = new Result();
        r.setCode(200);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }

    public static Result fail(String msg){
        return fail(500,msg,null);
    }

    public static Result fail(int code, String msg, Object data) {
        Result r = new Result();
        r.setCode(code);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }

    public static Result fail(String msg, Object data) {
        Result r = new Result();
        r.setCode(500);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }

    public static Result fail(int code, String msg) {
        Result r = new Result();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(null);
        return r;
    }

}

2. TokenException类(自定义异常)

继承RuntimeException异常类,RuntimeException属于非受检异常,仅在运行时捕获,编译时不会检查,因此可以不加try-catch语句直接抛出(使用见下文拦截器类JwtInterceptor)。

public class TokenException extends RuntimeException{
    public TokenException() {super();}

    public TokenException(String msg) {
        super(msg);
    }
}

3.GlobalExceptionHandler类(全局异常处理)

token失效状态码可以与前端做约定,一般使用401表示未经授权

@RestControllerAdvice
public class GlobalExceptionHandler {

    //token失效异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = TokenException.class)
    public Result handler(TokenException e){
        return Result.fail(401, e.getMessage());
    }

}

3、util包(业务工具包)

1. JwtUtil类

先导入依赖(采用jjwt)

        <!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

配置基本信息(写入application.yml,secret为密码,expire为过期时间,header为请求头名称)

markerhub:
  jwt:
    secret: 2019scaumis25710000de581c0f9eb5
    expire: 604800
    header: Authorization

编写Jwt工具类

@Data
@Component
@ConfigurationProperties(prefix = "markerhub.jwt")
public class JwtUtil {

    private String secret;
    private long expire;
    private String header;

    /**
     * 生成jwt token
     * @param session
     * @return
     */
    public String getToken(String session){
        Date nowDate = new Date();
        //过期时间
        Date expireDate = new Date(nowDate.getTime() + expire * 1000);

        return Jwts.builder()
                .setHeaderParam("typ","JWT")
                .setSubject(session)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();
    }


    /**
     * 从token中获取自定义登录态session后解密获取openid
     * @param token
     * @return
     */
    public String getOpenidFromToken(String token){
        String openid;
        String session;
        try{
            //解析token获取session
            C
  • 11
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值