当前位置:网站首页>Open source a set of minimalist front and rear end separation project scaffold
Open source a set of minimalist front and rear end separation project scaffold
2020-11-06 21:07:00 【itread01】
Preface
Fast Scaffold It's a very simple front and back separation project scaffold , Include a portal front end 、 One admin back-end , It can be used to quickly set up front and back end separation projects for secondary development
Technology stack
portal front end :vue + element-ui + avue, Use typescript Syntax encoding
admin back-end :springboot + mybatis-plus + mysql, Adopt jwt Authentication
Project structure
portal front end
Front end projects , It's us :Vue Project entry example , On this basis, I made a jump
introduce avue
avue, Based on element-ui Developed a front-end framework for many operations , We are, too test Test module Admin A simple test is done on the page
Official website :https://avuejs.com/
router To configure
router Routing configuration , newly added test Module menu routing ,beforeEach There is no token in the judgment , Jump to the login page
router.beforeEach(async(to, from, next) => { console.log(" Jump start , The goal is :"+to.path); document.title = `${to.meta.title}`; // No token , Jump to the login page if (to.name !== 'Login' && !TokenUtil.getToken()){ console.log(" No token , Jump to the login page "); next({ name: 'Login' }); } // Jump to page next(); });
store To configure
store To configure , newly added user Properties ,getters Provide getUser Method , as well as mutations、actions Of setUser Method
import Vue from 'vue' import Vuex from 'vuex' import User from "@/vo/user"; import CommonUtil from "@/utils/commonUtil"; import {Object} from "@/utils/commonUtil" import AxiosUtil from "@/utils/axiosUtil"; import TokenUtil from "@/utils/tokenUtil"; import SessionStorageUtil from "@/utils/sessionStorageUtil"; Vue.use(Vuex); /* Make an appointment , Components are not allowed to be changed directly store Example of state, It should be carried out action To distribute (dispatch) Event notification store To change */ export default new Vuex.Store({ state: { user:User, }, getters:{ getUser: state => { return state.user; } }, mutations: { SET_USER: (state, user) => { state.user = user; } }, actions: { async setUser({commit}){ let thid = this; console.log(" call getUserByToken Interface get login user !"); AxiosUtil.post(CommonUtil.getAdminUrl()+"/getUserByToken",{token:TokenUtil.getToken()},function (result) { let data = result.data as Object; commit('SET_USER', new User(data.id,data.username)); // Set to sessionStorage SessionStorageUtil.setItem("loginUser",thid.getters.getUser); }); } }, modules: { } })
Tool class encapsulation
axiosUtil.ts
Set global withCredentials,timeout
Set request Intercept , Set... In the request header token token
Set response Intercept , Set the unified response to the exception message prompt and jump to the login page when the token is invalid
It encapsulates post、get And so on , Convenient call
commonUtil.ts
It encapsulates the common use of 、 Common methods , For example, get the back-end service address 、 Get login users, etc
sessionStorageUtil.ts
Package sessionStorage Session level caching , Easy to set up cache
tokenUtil.ts
Package token Token utility class , Easy to set token Token to cookie
admin back-end
Back end projects , Using our :SpringBoot series ——MyBatis-Plus Integrated packaging , On this basis, adjustments have been made
Only keep tb_user Table module , Other tables and code modules don't need , Change the password to MD5 Encrypted storage
Configuration file
server: port: 10086 spring: application: name: admin datasource: # Database related url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mvc: format: date: yyyy-MM-dd HH:mm:ss jackson: date-format: yyyy-MM-dd HH:mm:ss #jackson Format the date arguments that respond back time-zone: GMT+8 portal: url: http://172.16.35.52:10010 # Front end address ( For cross domain configuration ) token: secret: huanzi-qch #token Encrypt the private key ( Very important , Keep it secret ) expire: time: 86400000 #token Effective time , Unit millisecond 24*60*60*1000
cors Security cross domain
establish MyConfiguration, Turn on cors Security cross domain , Details can be found in our previous blog :SpringBoot series ——CORS( Cross source resource sharing )
@Configuration public class MyConfiguration { @Value("${portal.url}") private String portalUrl; @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins(portalUrl) .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true).maxAge(3600); } }; } }
jwt Authentication
maven introduce jwt Rely on
<!-- JWT --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.5.0</version> </dependency>
JwtUtil Tool class , Package generation token, Check token, And according to token Get login users
/** * JWT Tool class */ @Component public class JwtUtil { /** * Expiration time , millisecond */ private static long TOKEN_EXPIRE_TIME; @Value("${token.expire.time}") public void setExpireTime(long expireTime) { JwtUtil.TOKEN_EXPIRE_TIME = expireTime; } /** * token Private key */ private static String TOKEN_SECRET; @Value("${token.secret}") public void setSecret(String secret) { JwtUtil.TOKEN_SECRET = secret; } /** * Generate signature */ public static String sign(String userId){ // Expiration time Date date = new Date(System.currentTimeMillis() + TOKEN_EXPIRE_TIME); // Private key and encryption algorithms Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // Set header information HashMap<String, Object> header = new HashMap<>(2); header.put("typ", "JWT"); header.put("alg", "HS256"); // With userID Generate signature return JWT.create().withHeader(header).withClaim("userId",userId).withExpiresAt(date).sign(algorithm); } /** * Verify signature */ public static boolean verity(String token){ // Token is empty if(StringUtils.isEmpty(token)){ return false; } try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); // Can I decrypt DecodedJWT jwt = verifier.verify(token); // Verification expiration time if(new Date().after(jwt.getExpiresAt())){ return false; } return true; } catch (IllegalArgumentException | JWTVerificationException e) { ErrorUtil.errorInfoToString(e); } return false; } /** * According to token Get users id */ public static String getUserIdByToken(String token){ try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); return jwt.getClaim("userId").asString(); } catch (IllegalArgumentException | JWTVerificationException e) { ErrorUtil.errorInfoToString(e); } return null; } }
Login interceptor
LoginFilter Login interceptor , Do not block login requests 、 Cross domain pre check request , All other requests are intercepted to check whether there is a token
PS: We have configured global security across domains , But in the interceptor ,PrintWriter.print Go back response, To manually add a response header tag to allow the other party to cross domain
// Mark the current request Party to allow cross domain access response.setHeader("Access-Control-Allow-Credentials","true"); response.setHeader("Access-Control-Allow-Headers","content-type, token"); response.setHeader("Access-Control-Allow-Methods","*"); response.setHeader("Access-Control-Allow-Origin",portalUrl);
/** * Login interceptor */ @Component public class LoginFilter implements Filter { @Value("${server.servlet.context-path:}") private String contextPath; @Value("${portal.url}") private String portalUrl; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String method = request.getMethod(); // Do not block login requests 、 Cross domain pre check request , All other requests are intercepted to check whether there is a token if (!"/login".equals(request.getRequestURI().replaceFirst(contextPath,"")) && !"options".equals(method.toLowerCase())) { String token = request.getHeader("token"); // Verify signature if(!JwtUtil.verity(token)){ String dataString = "{\"status\":401,\"message\":\" Invalid token token , Access failed , Please log in again !\"}"; // eliminate cookie Cookie cookie = new Cookie("PORTAL_TOKEN", null); cookie.setPath("/"); cookie.setMaxAge(0); response.addCookie(cookie); // Turn json A string is converted to Object thing , Set to Result And assign it to the return value , Remember to indicate that the current page can be accessed across domains response.setHeader("Access-Control-Allow-Credentials","true"); response.setHeader("Access-Control-Allow-Headers","content-type, token"); response.setHeader("Access-Control-Allow-Methods","*"); response.setHeader("Access-Control-Allow-Origin",portalUrl); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); out.print(dataString); out.flush(); out.close(); return; } } filterChain.doFilter(servletRequest, servletResponse); } }
Simple controller
IndexController controller , Provide three post Method :login Log in ,logout Log out ,getUserByToken Through token Token acquisition login user
@RestController @RequestMapping("/") @Slf4j public class IndexController { @Autowired private TbUserService tbUserService; /** * Log in */ @PostMapping("login") public Result<String> login(@RequestBody TbUserVo entityVo){ // Focus only on the user name 、 code if(StringUtils.isEmpty(entityVo.getUsername()) || StringUtils.isEmpty(entityVo.getPassword())){ return Result.build(400," Account number or password cannot be empty ......",""); } TbUserVo tbUserVo = new TbUserVo(); tbUserVo.setUsername(entityVo.getUsername()); // code MD5 After encryption, the ciphertext is stored , Match first MD5 Match after encryption tbUserVo.setPassword(MD5Util.getMD5(entityVo.getPassword())); Result<List<TbUserVo>> listResult = tbUserService.list(tbUserVo); if(Result.OK.equals(listResult.getStatus()) && listResult.getData().size() > 0){ TbUserVo userVo = listResult.getData().get(0); //token String token = JwtUtil.sign(userVo.getId()+""); return Result.build(Result.OK," Login successful !",token); } return Result.build(400," Account or password error ...",""); } /** * Log out */ @PostMapping("logout") public Result<String> logout(HttpServletResponse response){ // eliminate cookie Cookie cookie = new Cookie("PORTAL_TOKEN", null); cookie.setPath("/"); cookie.setMaxAge(0); response.addCookie(cookie); return Result.build(Result.OK," I drive this road , I planted this tree , To pass by from here on , leave token token !",""); } /** * Through token Token acquisition login user */ @PostMapping("getUserByToken") public Result<TbUserVo> getUserByToken(@RequestBody TbUserVo entityVo){ String userId = JwtUtil.getUserIdByToken(entityVo.getToken()); Result<TbUserVo> result = tbUserService.get(userId); result.getData().setPassword(null); return userId == null ? Result.build(500," Operation failed !",new TbUserVo()) : result; } }
Effect demonstration
Log in
This is a minimalist login page 、 Login function , No token , The route will block the jump to the login page
Save after successful login token Token to cookie in , And get login user information , Store in Store in
To solve the problem of rearranging the page Store Data loss , At the same time, store a copy of the data to sessionStorage Get it , Reading Store When there is no information , Read cache first , If there is , Set it back to Store in
Leave blank after successful login Store、sessionStorage
Home
Minimalist project homepage , Path /, Usually used as a project home page , Now the page is a simple welcome page , It includes several router-link Routing and logout button
test Test
Integrated vue Simple tests such as data binding
info Test
Get the current active configuration environment Branch , Test the configuration of the file
admin Test
element-ui Match up avue, It can be built quickly admin Background management page and function
Packaged deployment
portal front end
It's already configured package.json Archives
"scripts": { "dev": "vue-cli-service serve --mode dev", "test": "vue-cli-service test --mode test", "build": "vue-cli-service build --mode prod" },
At the same time ,vue.config.js The build path is configured in
publicPath: './', outputDir: 'dist', assetsDir: 'static',
Execute build command , It will be in package.json Under the directory at the same level , establish dist Folder , The generated files are in it
Put the generated files in Tomcat In containers or other containers , Execution container , front end portal Project complete deployment
admin back-end
pom The file has already set the package configuration
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <finalName>${project.artifactId}</finalName> <outputDirectory>package</outputDirectory> </configuration> </plugin>
maven Direct execution package command , It will be with pom File under the same level directory to create package Folder , Generated jar The bag is in it
Use java command :java -jar admin.jar, Execute jar package , back-end admin Project complete deployment
Postscript
A set of minimalist front and back separation project scaffolding is recorded here for the time being , I'll add it later
Open source code
notes :admin Back end database files are in admin Back end projects resources/sql Under the table of contents
The code is open source 、 Trust to my GitHub、 Code cloud :
GitHub:https://github.com/huanzi-qch/fast-scaffold
Code cloud :https://gitee.com/huanzi-qch/fast-scaffold
版权声明
本文为[itread01]所创,转载请带上原文链接,感谢
边栏推荐
- ES6 learning notes (4): easy to understand the new grammar of ES6
- ado.net和asp.net的关系
- An article will take you to understand SVG gradient knowledge
- 2020-09-03:裸写算法:回形矩阵遍历。
- How does filecoin's economic model and future value support the price of fil currency breaking through thousands
- C# 调用SendMessage刷新任务栏图标(强制结束时图标未消失)
- To teach you to easily understand the basic usage of Vue codemirror: mainly to achieve code editing, verification prompt, code formatting
- Metersphere developer's Manual
- Even liver three all night, jvm77 high frequency interview questions detailed analysis, this?
- Try to build my mall from scratch (2): use JWT to protect our information security and perfect swagger configuration
猜你喜欢
Some operations kept in mind by the front end foundation GitHub warehouse management
C# 调用SendMessage刷新任务栏图标(强制结束时图标未消失)
The importance of big data application is reflected in all aspects
Flink's datasource Trilogy 2: built in connector
What knowledge do Python automated testing learn?
An article will introduce you to HTML tables and their main attributes
事件监听问题
华为Mate 40 系列搭载HMS有什么亮点?
【ElasticSearch搜索引擎】
【转发】查看lua中userdata的方法
随机推荐
The legality of IPFs / filecoin: protecting personal privacy from disclosure
Bitcoin once exceeded 14000 US dollars and is about to face the test of the US election
【转发】查看lua中userdata的方法
Metersphere developer's Manual
ES6 learning notes (2): teach you to play with class inheritance and class objects
GUI engine evaluation index
How to hide part of barcode text in barcode generation software
Even liver three all night, jvm77 high frequency interview questions detailed analysis, this?
2020年第四届中国 BIM (数字建造)经理高峰论坛即将在杭举办
【:: 是什么语法?】
With this artifact, quickly say goodbye to spam messages
面试官: ShardingSphere 学一下吧
2020-09-09:裸写算法:两个线程轮流打印数字1-100。
Can you do it with only six characters?
递归、回溯算法常用数学基础公式
This project allows you to quickly learn about a programming language in a few minutes
Building a new generation cloud native data lake with iceberg on kubernetes
Network programming NiO: Bio and NiO
事务的隔离级别与所带来的问题
Axios learning notes (2): easy to understand the use of XHR and how to package simple Axios