您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

基于JWT的RuoYi开发框架与EMQX的系统集成方法

时间:2023-05-17 12:48:45  来源:今日头条  作者:研道鸠摩智
我们使用JwtAccessTokenConverter来生成Jwt Token,JwtAccessTokenConverter默认使用的Signer是macSigner,而MacSigner默认的算法是HMACSHA256。下面这个地方配置的Signkey一定要与上面配置EMQX的JWT认证时的Secret一致,这样就可以让EMQX验证Token的真假。

背景

RuoYi是一款基于Spring Boot、Spring Security和MyBatis的快速开发框架,它目前在中后台管理系统开发领域拥有大量的使用者。但是它当前缺少对websocket协议双工通信信道的支持,即长链接管理这块的功能比较弱。导致像实时通知(后台服务与前端的消息推送,异步通知),实时数据可视化(后端获取数据并推送到前端)等功能不好实现。本文介绍如果结合第三方消息代理服务器EMQX实现上述功能。

准备

Docker安装EMQX

docker pull emqx/emqx:5.0.21
 
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx:5.0.21

EMQX访问配置

登录EMQX后台,点击菜单Access Control ->Authentication。

新增配置,选择JWT,因为RuoYi的框架使用的是JWT,这个地方就是让EMQX的认证方式和RuoYi的一致,这样RuoYi返回的token,我们可以直接通过它来调用EMQX的restful接口,实现发送消息。

新增配置认证

登录EMQX管理后台,配置认证

其中JWT from这个地方我们选择username,然后算法根据你的RuoYi框架实现的token的算法,这个地方我们使用的是hmac.其他的实践可以查看这https://www.emqx.io/docs/en/v5.0/access-control/authn/jwt.html#configure-with-dashboard。

配置RuoYi的token生成

我们使用JwtAccessTokenConverter来生成jwt token,JwtAccessTokenConverter默认使用的signer是MacSigner,而MacSigner默认的算法是HMACSHA256。下面这个地方配置的signkey一定要与上面配置EMQX的JWT认证时的Secret一致,这样就可以让EMQX验证token的真假。

@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("mytokenkey");// token key
    converter.setAccessTokenConverter(new NewTokenConverter());
    return converter;
}

JwtAccessTokenConverter的signer

我们想扩展ken的额外信息,便可以继承AccessTokenConverter,写一个新的。在这个转化器中,我们可以加入额外的属性identity,这样token里面就包含了这些有用的信息。

package com.ruoyi.framework.auth.config.tokenconvert;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;

import JAVA.util.*;

public class NewTokenConverter implements AccessTokenConverter {
  private UserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
  private boolean includeGrantType;
  private String scopeAttribute = "scope";
  private String clientIdAttribute = "client_id";

  public NewTokenConverter() {
  }

  public void setUserTokenConverter(UserAuthenticationConverter userTokenConverter) {
    this.userTokenConverter = userTokenConverter;
  }

  public void setIncludeGrantType(boolean includeGrantType) {
    this.includeGrantType = includeGrantType;
  }

  public void setScopeAttribute(String scopeAttribute) {
    this.scopeAttribute = scopeAttribute;
  }

  public void setClientIdAttribute(String clientIdAttribute) {
    this.clientIdAttribute = clientIdAttribute;
  }

  public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
    Map<String, Object> response = new HashMap();
    OAuth2Request clientToken = authentication.getOAuth2Request();
    if (!authentication.isClientOnly()) {
      response.putAll(this.userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
    } else if (clientToken.getAuthorities() != null && !clientToken.getAuthorities().isEmpty()) {
      response.put("authorities", AuthorityUtils.authorityListToSet(clientToken.getAuthorities()));
    }

    if (token.getScope() != null) {
      response.put(this.scopeAttribute, token.getScope());
    }

    if (token.getAdditionalInformation().contAInsKey("jti")) {
      response.put("jti", token.getAdditionalInformation().get("jti"));
    }

    if (token.getExpiration() != null) {
      response.put("exp", token.getExpiration().getTime() / 1000L);
    }
    if (authentication.getName()!=null) {
      response.put("identity", authentication.getName());
    }

    if (this.includeGrantType && authentication.getOAuth2Request().getGrantType() != null) {
      response.put("grant_type", authentication.getOAuth2Request().getGrantType());
    }

    response.putAll(token.getAdditionalInformation());
    response.put(this.clientIdAttribute, clientToken.getClientId());
    if (clientToken.getResourceIds() != null && !clientToken.getResourceIds().isEmpty()) {
      response.put("aud", clientToken.getResourceIds());
    }

    return response;
  }

  public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map) {
    DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(value);
    Map<String, Object> info = new HashMap(map);
    info.remove("exp");
    info.remove("aud");
    info.remove(this.clientIdAttribute);
    info.remove(this.scopeAttribute);
    if (map.containsKey("exp")) {
      token.setExpiration(new Date((Long)map.get("exp") * 1000L));
    }

    if (map.containsKey("jti")) {
      info.put("jti", map.get("jti"));
    }

    token.setScope(this.extractScope(map));
    token.setAdditionalInformation(info);
    return token;
  }

  public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
    Map<String, String> parameters = new HashMap();
    Set<String> scope = this.extractScope(map);
    Authentication user = this.userTokenConverter.extractAuthentication(map);
    String clientId = (String)map.get(this.clientIdAttribute);
    parameters.put(this.clientIdAttribute, clientId);
    if (this.includeGrantType && map.containsKey("grant_type")) {
      parameters.put("grant_type", (String)map.get("grant_type"));
    }

    Set<String> resourceIds = new LinkedHashSet((Collection)(map.containsKey("aud") ? this.getAudience(map) : Collections.emptySet()));
    Collection<? extends GrantedAuthority> authorities = null;
    if (user == null && map.containsKey("authorities")) {
      String[] roles = (String[])((Collection)map.get("authorities")).toArray(new String[0]);
      authorities = AuthorityUtils.createAuthorityList(roles);
    }

    OAuth2Request request = new OAuth2Request(parameters, clientId, authorities, true, scope, resourceIds, (String)null, (Set)null, (Map)null);
    return new OAuth2Authentication(request, user);
  }

  private Collection<String> getAudience(Map<String, ?> map) {
    Object auds = map.get("aud");
    if (auds instanceof Collection) {
      Collection<String> result = (Collection)auds;
      return result;
    } else {
      return Collections.singleton((String)auds);
    }
  }

  private Set<String> extractScope(Map<String, ?> map) {
    Set<String> scope = Collections.emptySet();
    if (map.containsKey(this.scopeAttribute)) {
      Object scopeObj = map.get(this.scopeAttribute);
      if (String.class.isInstance(scopeObj)) {
        scope = new LinkedHashSet(Arrays.asList(((String)String.class.cast(scopeObj)).split(" ")));
      } else if (Collection.class.isAssignableFrom(scopeObj.getClass())) {
        Collection<String> scopeColl = (Collection)scopeObj;
        scope = new LinkedHashSet(scopeColl);
      }
    }

    return (Set)scope;
  }
}

客户端连接

npm install --save mqtt
 
import mqtt from 'mqtt'
createWebSocket() {

  const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)

  const host = process.env.VUE_App_WEB_SOCKET

  const options = {
    keepalive: 60,
    clientId: clientId,
    username:getToken(),
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: true,
    reconnectPeriod: 1000,
    connectTimeout: 30 * 1000,
    will: {
      topic: 'WillMsg',
      payload: 'Connection Closed abnormally..!',
      qos: 0,
      retain: false
    },
  }

  console.log('Connecting mqtt client')

  this.ws = mqtt.connect(host, options)

  this.ws.on('error', (err) => {
    console.log('Connection error: ', err)
    this.ws.end()
  })

  this.ws.on('reconnect', () => {
    console.log('Reconnecting...')
  })

  this.ws.on('connect', () => {
    console.log('Client connected:' + clientId)
    // Subscribe
    this.ws.subscribe(store.getters.topic, { qos: 0 })
  })

  // Received
  this.ws.on('message', (topic, message, packet) => {
    console.log('Received Message: ' + message.toString() + 'nOn topic: ' + topic)
  })
},

测试

我们测试收到了消息提示,同时打开console,看到我们连接成功了。

收到消息

连接log

参考链接

https://www.emqx.com/en/blog/connect-to-mqtt-broker-with-websocket。



Tags:框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  Search: 框架  点击:(8)  评论:(0)  加入收藏
Htmx,它到底是框架还是库?
在最近的前端开发技术的探讨中,htmx经常成为热议的话题。一些人批评它,认为尽管htmx批评现代前端框架过于复杂,但它自己却似乎也是一个复杂的框架。这种看法值得我们深入思考。...【详细内容】
2024-03-28  Search: 框架  点击:(16)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  Search: 框架  点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: 框架  点击:(39)  评论:(0)  加入收藏
Go Gin框架实现优雅地重启和停止
在Web应用程序中,有时候我们需要重启或停止服务器,无论是因为更新代码还是进行例行维护。在这种情景下,我们需要保证应用程序的可用性和数据的一致性。这就需要优雅地关闭和重...【详细内容】
2024-01-30  Search: 框架  点击:(67)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  Search: 框架  点击:(67)  评论:(0)  加入收藏
OpenHarmony - 基于ArkUI框架实现日历应用
前言对于刚刚接触OpenHarmony应用开发的开发者,最快的入门方式就是开发一个简单的应用,下面记录了一个日历应用的开发过程,通过日历应用的开发,来熟悉基本图形的绘制,ArkUI的组件...【详细内容】
2024-01-16  Search: 框架  点击:(54)  评论:(0)  加入收藏
阿里“AI替换万物”框架火爆社区,网友:偶像不需要真人了?
白交 发自 凹非寺量子位 | 公众号 QbitAIReplace Anything as you want。现在只需框住你需要保留的区域,AI就可以替换万物了!比如让霉霉穿上中国旗袍,结果发饰、服装、背景等各...【详细内容】
2024-01-15  Search: 框架  点击:(66)  评论:(0)  加入收藏
分布式事务框架选择与实践
分布式事务是处理跨多个服务的原子操作的关键概念,而选择适合应用场景的框架对于确保事务一致性至关重要。以下是几个常见的分布式事务框架,并讨论它们的使用和实践。1. XA协...【详细内容】
2024-01-05  Search: 框架  点击:(96)  评论:(0)  加入收藏
JavaScript前端框架2024年展望
Angular、Next.js、React和Solid的维护者和创作者们展望2024年,分享了他们计划中的改进。译自2024 Predictions by JavaScript Frontend Framework Maintainers,作者 Loraine...【详细内容】
2024-01-05  Search: 框架  点击:(89)  评论:(0)  加入收藏
▌简易百科推荐
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(12)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(53)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(50)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(67)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(84)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
花 15 分钟把 Express.js 搞明白,全栈没有那么难
Express 是老牌的 Node.js 框架,以简单和轻量著称,几行代码就可以启动一个 HTTP 服务器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都与 Express 息息相关。Express 框...【详细内容】
2024-01-16  程序员成功  微信公众号  Tags:Express.js   点击:(86)  评论:(0)  加入收藏
站内最新
站内热门
站内头条