SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序

您所在的位置:网站首页 vue小程序模板 SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序

SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序

2023-03-14 21:16| 来源: 网络整理| 查看: 265

SpringBoot2.X全栈开发医疗小程序 - SaToken认证与授权

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