当前位置:网站首页>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]所创,转载请带上原文链接,感谢
边栏推荐
- 2020年第四届中国 BIM (数字建造)经理高峰论坛即将在杭举办
- jenkins安装部署过程简记
- 2020-09-04:函数调用约定了解么?
- 【转发】查看lua中userdata的方法
- html+vue.js 實現分頁可相容IE
- What are the criteria for selecting a cluster server?
- How to turn data into assets? Attracting data scientists
- Can you do it with only six characters?
- [efficiency optimization] Nani? Memory overflow again?! It's time to sum up the wave!!
- 開源一套極簡的前後端分離專案腳手架
猜你喜欢

Bitcoin once exceeded 14000 US dollars and is about to face the test of the US election

ES中删除索引的mapping字段时应该考虑的点

An article will take you to understand CSS alignment

What is the tensor in tensorflow?

代码生成器插件与Creator预制体文件解析

What are manufacturing and new automation technologies?

Helping financial technology innovation and development, atfx is at the forefront of the industry

解决 WPF 绑定集合后数据变动界面却不更新的问题

意外的元素..所需元素..

谷歌浏览器实现视频播放加速功能
随机推荐
【ElasticSearch搜索引擎】
(2) ASP.NET Core3.1 Ocelot routing
html+vue.js 實現分頁可相容IE
An article takes you to understand CSS3 picture border
Git rebase is in trouble. What to do? Waiting line
Live broadcast preview | micro service architecture Learning Series live broadcast phase 3
An article will take you to understand CSS alignment
GUI engine evaluation index
Summary of front-end interview questions (C, s, s) that front-end engineers need to understand (2)
Multi robot market share solution
Filecoin has completed a major upgrade and achieved four major project progress!
Use modelarts quickly, zero base white can also play AI!
How does cglib implement multiple agents?
An article will take you to understand SVG gradient knowledge
開源一套極簡的前後端分離專案腳手架
What are Devops
What are the criteria for selecting a cluster server?
Take you to learn the new methods in Es5
Flink's datasource Trilogy: direct API
游戏开发中的新手引导与事件管理系统