微信小程序实现token登录

该文描述了微信小程序的登录流程,前端通过wx.login获取code,然后发送给后端。后端使用SpringSecurity,结合jwt和redis,通过code向微信服务器获取openid,查询数据库进行登录或注册操作。同时,文章详细展示了如何生成和验证jwttoken,并管理用户登录状态。
摘要由CSDN通过智能技术生成

微信小程序实现token登录

微信小程序登录流程

img

前端先调用wx.login()接口获取code,再把code发个后端

login() {
  wx.login({
    success: (res) => {
      wx.request({
          url:"/login",
          method:"POST",
          data:{
              code:res
          },
          dataType:'json'
      })
    },
  })
}

后端采用是SpringSecurity+jwt+redis

@RestController
public class LoginController {

    @Autowired
    LoginService loginService;

    @PostMapping("/login")
    public R<Map<String,Object>> login(@RequestBody Map<String, String> params) {
        String code = params.get("code");
        if (StringUtils.isBlank(code)) {
            throw new ValidateException("code不能为空");
        }

        return loginService.login(code);
    }
}

后端接收code之后再发送http到微信官方获得openid

@Component
@ConfigurationProperties(prefix = "wx")
@Data
public class WxLogin {
    private  String appid;
    private  String secret;
    private  String grant_type;
    private  String wxurl;


    public String getOpenId(String code) throws IOException, URISyntaxException {
        URL url = new URL(wxurl);
        URI uri = new URIBuilder().setScheme(url.getProtocol())
                .setHost(url.getHost())
                .setPort(url.getPort())
                .setPath(url.getPath())
                .setParameters(
                        new BasicNameValuePair("appid",appid),
                        new BasicNameValuePair("secret",secret),
                        new BasicNameValuePair("js_code",code),
                        new BasicNameValuePair("grant_type",grant_type)
                ).build();

        HttpGet httpGet = new HttpGet(uri);

        CloseableHttpClient httpClient = HttpClientBuilder.create().build();

        // 配置信息
        RequestConfig requestConfig = RequestConfig.custom()
                // 设置连接超时时间(单位毫秒)
                .setConnectTimeout(5000)
                // 设置请求超时时间(单位毫秒)
                .setConnectionRequestTimeout(5000)
                // socket读写超时时间(单位毫秒)
                .setSocketTimeout(5000)
                // 设置是否允许重定向(默认为true)
                .setRedirectsEnabled(true).build();

        httpGet.setConfig(requestConfig);

        //响应
        CloseableHttpResponse response = httpClient.execute(httpGet);

        // 从响应模型中获取响应实体
        String openId = null;
        try {
            HttpEntity responseEntity = response.getEntity();
//            System.out.println("响应状态为:" + response.getStatusLine());
            if (StringUtils.isNull(responseEntity)) {
                throw new WxLoginException("响应体为空");
            }
//            System.out.println("响应内容长度为:" + responseEntity.getContentLength());
//            System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
            JSONObject json = JSON.parseObject(EntityUtils.toString(responseEntity));
            openId = json.getString("openid");
            if (StringUtils.isNull(openId)) {
                throw new WxLoginException("openid重复使用");
            }
        } finally {
            httpClient.close();
            response.close();
        }

        return openId;

    }
}

通过opid查询数据库进行登录注册。

springsecurity登录流程

fd1374d3fe6d40d69f1c6500239308b2

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private WxLogin wxLogin;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    @Override
    public R<Map<String, Object>> login(String code) {
        //得到openID
        String openId = null;
        try {
            openId = wxLogin.getOpenId(code);
        } catch (IOException | URISyntaxException e) {
            e.printStackTrace();
            return R.fail(HttpStatus.ERROR,"服务器请求异常");
        }
        //查询数据库是否存在openID
        boolean exist = true;
        //先查缓存再查数据库
        LoginUser loginUser = redisCache.getCacheObject(RedisCacheKeys.USERINFO_KEY + openId);
        if (StringUtils.isNull(loginUser)) {
            loginUser = new LoginUser();
        }
        if (StringUtils.isNull(loginUser.getUser())) {
            LambdaQueryWrapper<User> uqw = new LambdaQueryWrapper<>();
            uqw.eq(User::getOpenid,openId);
            User user = userService.getOne(uqw);
            if (StringUtils.isNull(user)) {
                exist = false;
            } else {
                loginUser.setUser(user);
                redisCache.setCacheObject(RedisCacheKeys.USERINFO_KEY+openId, loginUser);
            }
        }
        //不存在就注册,再登录
        if(!exist) {
            register(openId);
        }
        //springsecurity进行登录,存在就登录
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(openId, "null");
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        if (StringUtils.isNull(authenticate)) {
            throw new UsernameNotFoundException("用户名不出在!");
        }

        //封装
        LoginUser user = (LoginUser) authenticate.getPrincipal();

        //生成token
        String token = tokenService.createToken(user);
        UserInfoVo userInfo = BeanUtils.copyBean(loginUser.getUser(),UserInfoVo.class);

        HashMap<String, Object> res = new HashMap<String, Object>();
        res.put("token",token);
        res.put("userInfo",userInfo);
        return R.ok(res);
    }

    @Override
    public void register(String openId) {
        //
        User user = new User();
        user.setOpenid(openId);
        userService.save(user);
    }
}
token生成
@Component
public class TokenService
{
    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    // 令牌有效期(默认30分钟)
    @Value("${token.expireTime}")
    private int expireTime;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    protected static final long MILLIS_HOURS = 60 * MILLIS_MINUTE;

    private static final Long MILLIS_MINUTE_TEN = 60 * 60 * 1000L;

    @Autowired
    private RedisCache redisCache;

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request)
    {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            try
            {
                Claims claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String openid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(openid);
                return redisCache.getCacheObject(userKey);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token)
    {
        if (StringUtils.isNotEmpty(token))
        {
            String userKey = getTokenKey(token);
            redisCache.deleteObject(userKey);
        }
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, loginUser.getUser().getOpenid());
        return createToken(claims);
    }

    /**
     * 验证令牌有效期,相差不足一个小时,自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginUser loginUser)
    {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser)
    {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_HOURS);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getUser().getOpenid());
        redisCache.setCacheObject(userKey, loginUser,
                expireTime,TimeUnit.HOURS);
    }



    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims)
    {
        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token)
    {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token)
    {
        Claims claims = parseToken(token);
        return claims.getSubject();
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request)
    {
        return request.getHeader(header);
    }

    private String getTokenKey(String openId)
    {
        return RedisCacheKeys.USERINFO_KEY + openId;
    }
}

    

完整流程图

image-20230305231830838

**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request)
{
return request.getHeader(header);
}

private String getTokenKey(String openId)
{
    return RedisCacheKeys.USERINFO_KEY + openId;
}

}


完整流程图

[外链图片转存中...(img-KPgnRjWY-1678029553423)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值