SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 |
您所在的位置:网站首页 › vue小程序模板 › SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 |
SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 下栽の地止:https://lexuecode.com/6186.html 相比于 Http 的单项通信方式,WebSocket 可以从服务器向浏览器主动推送消息,这一特性可以帮助我们完成诸如 订单消息推送、IM实时聊天 等一些特定业务。然而 WebSocket 本身对“身份认证”并没有提供直接的支持,对客户端的连接默认是“来者不拒”,所以认证授权这个事,得我们自己动手。Sa-Token 是一个 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。GitHub 开源地址:http://github.com/dromara/sa-…下面我们介绍一下如何在 WebSocket 中集成 Sa-Token 身份认证,保证连接的安全性。两种集成方式我们将依次介绍目前最常见的两种集成 WebSocket 方式: Java 原生版:javax.websocket.SessionSpring 封装版:WebSocketSession 废话不多说,直接开搞:方式一:Java 原生版 javax.websocket.Session1、首先是引入 pom.xml 依赖 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-websocket cn.dev33 sa-token-spring-boot-starter 1.29.0复制代码2、登录接口,用于获取会话token/** * 登录测试 */@RestController@RequestMapping("/acc/")public class LoginController { // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456 @RequestMapping("doLogin") public SaResult doLogin(String name, String pwd) { // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对 if("zhang".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); return SaResult.ok("登录成功").set("token", StpUtil.getTokenValue()); } return SaResult.error("登录失败"); } // ... }复制代码3、WebSocket连接处理@Component@ServerEndpoint("/ws-connect/{satoken}")public class WebSocketConnect { /** * 固定前缀 */ private static final String USER_ID = "user_id_"; /** * 存放Session集合,方便推送消息 (javax.websocket.Session) */ private static ConcurrentHashMap sessionMap = new ConcurrentHashMap(); // 监听:连接成功 @OnOpen public void onOpen(Session session, @PathParam("satoken") String satoken) throws IOException { // 根据 token 获取对应的 userId Object loginId = StpUtil.getLoginIdByToken(satoken); if(loginId == null) { session.close(); throw new SaTokenException("连接失败,无效Token:" + satoken); } // put到集合,方便后续操作 long userId = SaFoxUtil.getValueByType(loginId, long.class); sessionMap.put(USER_ID + userId, session); // 给个提示 String tips = "Web-Socket 连接成功,sid=" + session.getId() + ",userId=" + userId; System.out.println(tips); sendMessage(session, tips); } // 监听: 连接关闭 @OnClose public void onClose(Session session) { System.out.println("连接关闭,sid=" + session.getId()); for (String key : sessionMap.keySet()) { if(sessionMap.get(key).getId().equals(session.getId())) { sessionMap.remove(key); } } } // 监听:收到客户端发送的消息 @OnMessage public void onMessage(Session session, String message) { System.out.println("sid为:" + session.getId() + ",发来:" + message); } // 监听:发生异常 @OnError public void onError(Session session, Throwable error) { System.out.println("sid为:" + session.getId() + ",发生错误"); error.printStackTrace(); } // --------- // 向指定客户端推送消息 public static void sendMessage(Session session, String message) { try { System.out.println("向sid为:" + session.getId() + ",发送:" + message); session.getBasicRemote().sendText(message); } catch (IOException e) { throw new RuntimeException(e); } } // 向指定用户推送消息 public static void sendMessage(long userId, String message) { Session session = sessionMap.get(USER_ID + userId); if(session != null) { sendMessage(session, message); } } }复制代码4、WebSocket配置/** * 开启WebSocket支持 */@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } 复制代码5、启动类@SpringBootApplicationpublic class SaTokenWebSocketApplication { public static void main(String[] args) { SpringApplication.run(SaTokenWebSocketApplication.class, args); } }复制代码搭建完毕,启动项目6、测试1、首先我们访问登录接口,拿到会话tokenhttp://localhost:8081/acc/doLogin?name=zhang&pwd=123456复制代码如图所示: 2、然后我们随便找一个WebSocket在线测试页面进行连接,例如:http://www.bejson.com/httputil/we…连接地址:ws://localhost:8081/ws-connect/302ee2f8-60aa-42aa-8ecb-eeae5ba57015复制代码如图所示: 3、如果我们输入一个错误的token,会怎样呢? 可以看到,连接会被立即断开!方式二:Spring 封装版:WebSocketSession1、同上:首先是引入 pom.xml 依赖 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-websocket cn.dev33 sa-token-spring-boot-starter 1.29.0复制代码2、登录接口,用于获取会话token/** * 登录测试 */@RestController@RequestMapping("/acc/")public class LoginController { // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456 @RequestMapping("doLogin") public SaResult doLogin(String name, String pwd) { // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对 if("zhang".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); return SaResult.ok("登录成功").set("token", StpUtil.getTokenValue()); } return SaResult.error("登录失败"); } // ... }复制代码3、WebSocket 连接处理/** * 处理 WebSocket 连接 */public class MyWebSocketHandler extends TextWebSocketHandler { /** * 固定前缀 */ private static final String USER_ID = "user_id_"; /** * 存放Session集合,方便推送消息 */ private static ConcurrentHashMap webSocketSessionMaps = new ConcurrentHashMap(); // 监听:连接开启 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // put到集合,方便后续操作 String userId = session.getAttributes().get("userId").toString(); webSocketSessionMaps.put(USER_ID + userId, session); // 给个提示 String tips = "Web-Socket 连接成功,sid=" + session.getId() + ",userId=" + userId; System.out.println(tips); sendMessage(session, tips); } // 监听:连接关闭 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { // 从集合移除 String userId = session.getAttributes().get("userId").toString(); webSocketSessionMaps.remove(USER_ID + userId); // 给个提示 String tips = "Web-Socket 连接关闭,sid=" + session.getId() + ",userId=" + userId; System.out.println(tips); } // 收到消息 @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException { System.out.println("sid为:" + session.getId() + ",发来:" + message); } // ----------- // 向指定客户端推送消息 public static void sendMessage(WebSocketSession session, String message) { try { System.out.println("向sid为:" + session.getId() + ",发送:" + message); session.sendMessage(new TextMessage(message)); } catch (IOException e) { throw new RuntimeException(e); } } // 向指定用户推送消息 public static void sendMessage(long userId, String message) { WebSocketSession session = webSocketSessionMaps.get(USER_ID + userId); if(session != null) { sendMessage(session, message); } } }复制代码4、WebSocket 前置拦截器/** * WebSocket 握手的前置拦截器 */public class WebSocketInterceptor implements HandshakeInterceptor { // 握手之前触发 (return true 才会握手成功 ) @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map attr) { System.out.println("---- 握手之前触发 " + StpUtil.getTokenValue()); // 未登录情况下拒绝握手 if(StpUtil.isLogin() == false) { System.out.println("---- 未授权客户端,连接失败"); return false; } // 标记 userId,握手成功 attr.put("userId", StpUtil.getLoginIdAsLong()); return true; } // 握手之后触发 @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { System.out.println("---- 握手之后触发 "); } }复制代码5、WebSocket 配置/** * WebSocket 相关配置 */@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer { // 注册 WebSocket 处理器 @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry // WebSocket 连接处理器 .addHandler(new MyWebSocketHandler(), "/ws-connect") // WebSocket 拦截器 .addInterceptors(new WebSocketInterceptor()) // 允许跨域 .setAllowedOrigins("*"); } }复制代码6、启动类/** * Sa-Token 整合 WebSocket 鉴权示例 */@SpringBootApplicationpublic class SaTokenWebSocketSpringApplication { public static void main(String[] args) { SpringApplication.run(SaTokenWebSocketSpringApplication.class, args); } }复制代码启动项目,开始测试7、测试1、首先访问登录接口,拿到会话tokenhttp://localhost:8081/acc/doLogin?name=zhang&pwd=123456复制代码如图所示: 2、然后打开WebSocket在线测试页面进行连接,例如:http://www.bejson.com/httputil/we…连接地址:ws://localhost:8081/ws-connect?satoken=fe6e7dbd-38b8-4de2-ae05-cda7e36bf2f7复制代码如图所示: 注:这里采用 url 传递 Token 是因为在第三方测试页面上这样比较方便,真实项目中可以从Cookie、Header参数、url参数 三种方式任选其一传递会话令牌,效果同等3、如果输入一个错误的 Token SpringBoot2.X全栈开发医疗小程序 - Vue项目技术栈围绕Vue.js框架,涉及到的常用技术/插件有: 1. vue-cli快速构建vue项目的脚手架工具,使用方式如下: 安装并构建项目1. npm install -g vue-cli2. 选择简单模板搭建vue项目:vue init webpack-simple my-webpack-simple-demo3. 选择webpack(略复杂)模板搭建vue项目: vue init webpack my-webpack-demo 建立好项目之后,如下启动:1. npm install2. npm run dev3. 打开游览器访问localhost:8080 如果在发布环境,需要生成build文件,运行命令npm run build。复制2. npm, webpack, babel, es6Vue开发中,会用到很多依赖包,传统方式是用标签引入,但是,在大型项目中更推荐使用npm安装。npm能够很好得和webpack等模块打包器配合使用。同时,vue提供单文件组件开发模式,这样,更需要webpack的配合,对.vue文件进行解析编译。 Vue中推荐使用ES6语法,这就需要编码器Babel的协助,而webpack对Babel支持良好,因此,webpack的重要性不言而喻,围绕着它,可以支持众多功能。 3. vue-routerVue非常适合单页面(SPA)应用开发,而单页面应用的核心是路由和模板。Vue核心库本身提供模板机制(mushtache),路由支持则由第三方库vue-router提供(Vue-router 2.0- 中文文档)。 这充分体现出Vue的“增量式开发设计”:Vue.js只提供核心功能,其他辅助功能由第三方库支持。 4. axiosVue更新到2.0之后,作者就宣告不再对vue-resource更新,而是推荐axios。axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。 由于axios并不是针对vue框架开发的,因此,如果将其使用在vue框架中,建议如下配置: import Vue from 'vue';import axios from 'axios';...Vue.prototype.$http = axios; 使用:this.$http.post(url, data).then((response) => {...});复制5. VuexVuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 Vue父子组件是单向通信的,由父组件向子组件传递数据。如果子组件要改变父组件状态,或者组件间有通信,那么,需要采用事件emit。但是,一旦组件数量庞大起来,通信更加复杂,那么,事件监听模式就显得散乱,无秩序,无法统一管理。这时,需要Vuex集中存储组件状态,并更新组件。 6. UI库支持Vue2.0的UI库很多,可以去网上搜搜。笔者推荐两个: element-ui:http://element.eleme.io/#/zh-CN/component/installation iview:https://www.iviewui.com/ 7. 调试工具Vue有基于chrome插件的调试工具:https://github.com/vuejs/vue-devtools 注意:一定要在vue项目中开启debugger模式,才能激活该调试工具: Vue.config.devtools = true;复制;vue-devtool.jpg SpringBoot2.X+Vue+UniAPP医疗小程序 - Vue3.0+ElementPlus实现系统登录1 步骤说明创建项目web >vue create web30_3》创建过程中选项 选择 Manually select features 》创建过程中选项 选择Babel,Router, Vuex 》创建过程中选项 选择版本3.0》创建过程中选项 选择In dedicated config files 》创建过程中选项 如果保存选择,则对 Save this as a preset for future project 选择yescd web30_3 npm run serve 安装插件 >npm i element-plus --save 注--save作用是项目拷贝时已经有包保存修改src下main.js文件,见编程说明部分2.12 编程说明2.1 main.js 修改说明增加两行内容 import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' 再用use引入。这点与vue2.0 编程一致。 import {createApp} from 'vue'import App from './App.vue'import router from './router'import store from './store'import ElementPlus from 'element-plus'import 'element-plus/lib/theme-chalk/index.css' createApp(App).use(store).use(router).use(ElementPlus).mount(rootContainer:'#app') 2.2 设计login页面设计表单,形成为独立Login页面,可以单独建立目录来存放,如后台 /back,则该文件为src/views/back/Login.vue 全部代码为: 提交 重置 export default { data() { var checkAge = (rule, value, callback) => { if (!value) { return callback(new Error('年龄不能为空')); } setTimeout(() => { if (!Number.isInteger(value)) { callback(new Error('请输入数字值')); } else { if (value < 18) { callback(new Error('必须年满18岁')); } else { callback(); } } }, 1000); }; var validatePass = (rule, value, callback) => { if (value === '') { callback(new Error('请输入密码')); } else { if (this.ruleForm.checkPass !== '') { this.$refs.ruleForm.validateField('checkPass'); } callback(); } }; var validatePass2 = (rule, value, callback) => { if (value === '') { callback(new Error('请再次输入密码')); } else if (value !== this.ruleForm.pass) { callback(new Error('两次输入密码不一致!')); } else { callback(); } }; return { ruleForm: { pass: '', checkPass: '', age: '' }, rules: { pass: [ { validator: validatePass, trigger: 'blur' } ], checkPass: [ { validator: validatePass2, trigger: 'blur' } ], age: [ { validator: checkAge, trigger: 'blur' } ] } }; }, methods: { submitForm(formName) { alert(1) this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } 2.3 App.vue 嵌入login页面登录跳转全文件如下: Home | 登录 跳转 #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50;} #nav { padding: 30px;} #nav a { font-weight: bold; color: #2c3e50;} #nav a.router-link-exact-active { color: #42b983;} 2.4 修改路由文件router/index.js 需要配置网页路由 ,{ path: '/Login', name: 'Login', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/back/Login.vue')}形成为如下文件 import { createRouter, createWebHashHistory } from 'vue-router'import Home from '../views/Home.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, { path: '/Login', name: 'Login', component: ()=>import('../views/back/Login') }] const router = createRouter({ history: createWebHashHistory(), routes}) export default router 3 编译运行和测试D:\workspace_vue\web30_3>npm run serve > [email protected] serve D:\workspace_vue\web30_3> vue-cli-service serve INFO Starting development server...98% after emitting CopyPlugin DONE Compiled successfully in 7941ms App running at: - Local: http://localhost:8080/ - Network: http://192.168.137.168:8080/ Note that the development build is not optimized. To create a production build, run npm run build.现在可以输入网址,http://192.168.137.168:8080/,在浏览器可以看到首页Home,再点击登录,可以进入登录页面。后面就是后台的对接工作了。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |