# springbootdemo **Repository Path**: webrx/springbootdemo ## Basic Information - **Project Name**: springbootdemo - **Description**: springboot vue3 项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-09-26 - **Last Updated**: 2023-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ERP项目开发 项目源码参考:git clone https://gitee.com/webrx/springbootdemo.git ## 后端数据库 ```sql -- 建立数据库 create database dbwx default charset utf8; use dbwx; -- 建立临时表,用于存储会话验证码 create table t_captcha( id char(36) primary key, value varchar(10), create_time timestamp default CURRENT_TIMESTAMP on UPDATE CURRENT_TIMESTAMP )engine=memory; -- 定义事件1分钟就会删除 create event capdel on schedule every 1 minute do delete from t_captcha where create_time < adddate(now(),interval -1 minute); ``` ## 后端接口 ### api后端接口 项目 #### 建立项目2.7.16 > 第一步:pom.xml > ```xml 4.0.0 org.example springbootdemo 1.0 ch01hello 17 17 UTF-8 org.springframework.boot spring-boot-dependencies 2.7.16 pom import org.yaml snakeyaml 2.0 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin 2.7.16 org.projectlombok lombok ``` #### 建立入口程序App.java `cn.webrx.App` ```java package cn.webrx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } ``` debug方式启动运行App ![image-20230926111524184](./assets/image-20230926111524184.png) ![image-20230926111547431](./assets/image-20230926111547431.png) #### 编写接口 `cn.webrx.controller.UserController` ```java package cn.webrx.controller; import org.springframework.boot.SpringBootVersion; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @GetMapping public String index() { return SpringBootVersion.getVersion(); } } ``` #### 浏览接口 `springboot web项目,默认内嵌了tomcat ,端口号为8080` http://localhost:8080/user ![image-20230926111724632](./assets/image-20230926111724632.png) ### 建立api项目3.1.4 #### pom.xml ```xml 4.0.0 org.example springbootdemo 1.0 ch02demo 17 17 UTF-8 org.springframework.boot spring-boot-dependencies 3.1.4 pom import org.yaml snakeyaml 2.0 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin 3.1.4 org.projectlombok lombok ``` #### App.java ```java package cn.webrx; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootVersion; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class App { @GetMapping("/") public String version(){ return SpringBootVersion.getVersion(); } public static void main(String[] args) { SpringApplication.run(App.class,args); } } ``` #### 启动App ![image-20230926112531508](./assets/image-20230926112531508.png) ![image-20230926112544315](./assets/image-20230926112544315.png) ### api项目建立 #### pom.xml ```xml 4.0.0 org.example springbootdemo 1.0 api 17 17 UTF-8 org.springframework.boot spring-boot-dependencies 2.7.16 pom import org.yaml snakeyaml 2.0 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test com.baomidou mybatis-plus-boot-starter 3.5.3.2 mysql mysql-connector-java 8.0.33 com.auth0 java-jwt 4.4.0 com.alibaba druid-spring-boot-starter 1.2.19 org.springframework.boot spring-boot-maven-plugin 2.7.16 org.projectlombok lombok ``` #### resources/application.yml ```yml server: port: 8080 spring: datasource: druid: url: jdbc:mysql:/wxdb username: root driver-class-name: com.mysql.cj.jdbc.Driver password: ``` #### App.java ```java package cn.webrx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } ``` ### jwt token #### 简介 WT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换 使用起来就是,由服务端根据规范生成一个令牌(token),并且发放给客户端。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。 作用:类似session保持登录状态 的办法,通过token来代表用户身份。 #### 生成校验 ![image-20230926115001020](./assets/image-20230926115001020.png) ![image-20230926115021085](./assets/image-20230926115021085.png) #### 优点 jwt基于json,非常方便解析可以再令牌中自定义丰富的内容,易扩展(payload可以扩展)通过签名,让JWT防止被篡改,安全性高资源服务使用JWT可不依赖认证服务即可完成授权 #### api token ```xml com.auth0 java-jwt 4.4.0 ``` #### jwt demo ```java public class JwtDemo { public static void main(String[] args) { //jwt 官方网站 https://jwt.io/ String SECRET = "webrx"; HashMap headers = new HashMap<>(); // 过期时间,60s Calendar expires = Calendar.getInstance(); expires.add(Calendar.SECOND, 3600); String jwtToken = JWT.create() .withHeader(headers)// 第一部分Header .withClaim("userId", 007)// 第二部分Payload .withClaim("userName", "admin") .withExpiresAt(expires.getTime())//有效期 .sign(Algorithm.HMAC256(SECRET));// 第三部分Signature System.out.println(jwtToken); // 创建一个验证的对象,必须使用签名密钥字符串 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjcsInVzZXJOYW1lIjoiYWRtaW4iLCJleHAiOjE2ODEyMDE3ODh9.Z8hTYjgRG1_Bd7iWav36xR1yXdmIokJLk81g2DW5tXY"; try { DecodedJWT verify = jwtVerifier.verify(token); System.out.println(verify.getClaim("userId").asInt()); System.out.println(verify.getClaim("userName").asString()); System.out.println("过期时间:" + verify.getExpiresAt()); } catch (TokenExpiredException e) { //输出过期对象Instant ZonedDateTime zonedDateTime = e.getExpiredOn().atZone(ZoneId.of("GMT+08:00")); LocalDateTime time = zonedDateTime.toLocalDateTime(); System.out.println(time); System.out.println(time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); System.out.println(e.getExpiredOn()); } catch (Exception e) { System.out.println("未知错误"); } } } ``` ```java package cn.webrx; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; @SpringBootApplication @Slf4j public class App implements ApplicationRunner { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Override public void run(ApplicationArguments args) throws Exception { //log.info("hello world "+ SpringBootVersion.getVersion()); //-------------------------生成令牌 //令牌有效期 10分钟 Date date = new Date(System.currentTimeMillis()+1000*60*10); //签名 String secret = "webrx"; JWTCreator.Builder builder = JWT.create(); Map header = new HashMap<>(); header.put("alg","HS256"); header.put("typ","JWT"); String t1 = builder.withHeader(header).withClaim("name","张三丰").withExpiresAt(date).sign(Algorithm.HMAC256(secret)); System.out.println(t1); // //-------------------------校验令牌 //String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoi5byg5LiJ5LiwIiwiZXhwIjoxNjk1NzEwNzc2fQ.12x9-83lQ6Du_NmEAmbaFTMADGT-HBafvii4IcfslKM"; String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoi5byg5LiJ5LiwIiwiZXhwIjoxNjk1NzEwNzc2fQ.12x9-83lQ6Du_NmEAmbaFTMADGT-HBafvii4IcfslKM"; //检查 JWTVerifier jwtver = JWT.require(Algorithm.HMAC256(secret)).build(); try { DecodedJWT dec = jwtver.verify(token); System.out.printf("%tF %()) // Header 格式规范 //.withClaim("userId", 21) // Payload 数据 //.withClaim("userName", "admin") // .withExpiresAt(calendar.getTime()) // 参数要求是java.util.Date 实例 ,过期时间 // .sign(Algorithm.HMAC256("webrx")); // 签名用的webrx //System.out.println(token); } } ``` ### 验证码实现 ```xml com.github.whvcse easy-captcha 1.6.2 org.openjdk.nashorn nashorn-core 15.4 ``` 生成验证码图片 ```java package cn.webrx.controller; import cn.webrx.service.CaptchaService; import com.wf.captcha.ArithmeticCaptcha; import com.wf.captcha.ChineseGifCaptcha; import com.wf.captcha.GifCaptcha; import com.wf.captcha.SpecCaptcha; import com.wf.captcha.base.Captcha; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.UUID; @RestController public class CaptchaController { @Autowired CaptchaService cs; @GetMapping("/check") public Map check() { Map map = new HashMap<>(); map.put("code", "200"); map.put("msg", "验证码"); //验证码key String checkkey = UUID.randomUUID().toString(); map.put("checkkey", checkkey); Random rand = new Random(); int num = rand.nextInt(1,5); //中文Gif动态验证码 Captcha captcha = switch (num) { case 1 -> new ChineseGifCaptcha(130, 40); case 2 -> new SpecCaptcha(130, 40); case 3 -> new ArithmeticCaptcha(130, 40); default -> new GifCaptcha(130, 40); }; int len = rand.nextInt(2,6); captcha.setLen(len); //验证码 map.put("check", captcha.toBase64()); //保存验证码的值 String value = captcha.text(); System.out.println(value); //checkkey captcha.text() redis缓存 mysql memory cn.webrx.entity.Captcha cc = new cn.webrx.entity.Captcha(); cc.setId(checkkey); cc.setValue(value); cs.save(cc); return map; } } ``` 数据库 ```sql -- 建立临时表,用于存储会话验证码 create table t_captcha( id char(36) primary key, value varchar(10), create_time timestamp default CURRENT_TIMESTAMP on UPDATE CURRENT_TIMESTAMP )engine=memory; -- 定义事件1分钟就会删除 create event capdel on schedule every 1 minute do delete from t_captcha where create_time < adddate(now(),interval -1 minute); ``` vue ```js ``` ![image-20230928091543251](./assets/image-20230928091543251.png) ### 加密 基本的l加密方法md5 sha ```java String p2(String str,String type) throws NoSuchAlgorithmException { StringBuilder sbu = new StringBuilder(); MessageDigest md = MessageDigest.getInstance(type); for(byte b : md.digest(str.getBytes())){ sbu.append(String.format("%02x",b)); } return sbu.toString(); } @Test void p1() throws NoSuchAlgorithmException { String s = "123"; String p1 = p2(s,"md5"); String p2 = p2(s,"sha"); System.out.println(s); System.out.println(p1); System.out.println(p2); StringBuilder sbu = new StringBuilder(); sbu.append(p1.substring(0,5)); sbu.append(p2.substring(0,5)); sbu.append(p1.substring(10,20)); sbu.append(p2.substring(10,20)); sbu.append(p1.substring(p1.length()-1)); sbu.append(p2.substring(p2.length()-1)); System.out.println(sbu); System.out.println(sbu.toString().length()); } ``` 加盐,动态密文 ```xml org.springframework.security spring-security-core 6.1.4 ``` ```java @Test void p4(){ //60位密文 System.out.println("$2a$10$SRZ8mKoF1yWpD0BkPKZzM.Dvc5Tgu.mFRoDm3ArotZ9RGuZkbLb/G".length()); System.out.println("$2a$10$QR4BJxq0z8XglOVCJvJguOvTKFxMixWFYeKcKIBXJpyyqqbiGoihq".length()); } @Test void p3() { PasswordEncoder pe = new BCryptPasswordEncoder(); for (int i = 0; i < 5; i++) { String encode = pe.encode("123"); //生成密文 System.out.println(encode); } //检查密码123 和密文是 System.out.println(pe.matches("123","$2a$10$CUAjmKHLSY6xwbxWbNaxN.9uICxr0IUxGzczbg08AFhpvT4dkv3jW")); } ``` ### api项目登录token ## 前端项目 ### 建立vue3 项目 > 第一步:npm create vue app ![image-20230926160206002](./assets/image-20230926160206002.png) > 第二步:添加项目依赖 ```cmd npm i element-plus axios @element-plus/icons-vue --save npm install -D unplugin-vue-components unplugin-auto-import ``` > 第三步:配置项目开发环境 ```text .env .env.development #开发 .env.production #生产环境 ``` > 第四步:配置vite.config.js项目配置文件 ```js import {fileURLToPath, URL} from 'node:url' import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import {ElementPlusResolver} from 'unplugin-vue-components/resolvers' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } //--------------------------------- server配置 , server: { host: '0.0.0.0', port: 80, open: true, proxy: { // 本地开发环境通过代理实现跨域,生产环境使用 nginx 转发 // 正则表达式写法 axios.defaults.baseURL = '/api' '/api': { target: 'http://localhost:8080', // 后端服务实际地址 http://localhost:8080/dbs url写成 /api/dbs changeOrigin: true, //开启代理 rewrite: (path) => path.replace(/^\/api/, '') } } } //----------------------------------- }) ``` > 第五步:main.js ```js import './assets/main.css' import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import router from './router' const app = createApp(App) app.use(createPinia()) app.use(router) app.mount('#app') ``` ### 配置项目 ### 封装axios工具类 ### 前端项目登录