当前位置:网站首页>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]所创,转载请带上原文链接,感谢
边栏推荐
- Flink's datasource Trilogy: direct API
- How to understand Python iterators and generators?
- The importance of big data application is reflected in all aspects
- Elasticsearch Part 6: aggregate statistical query
- To teach you to easily understand the basic usage of Vue codemirror: mainly to achieve code editing, verification prompt, code formatting
- 2020-09-03:裸写算法:回形矩阵遍历。
- What is the meaning of sector sealing of filecoin mining machine since the main network of filecoin was put online
- The legality of IPFs / filecoin: protecting personal privacy from disclosure
- Swagger 3.0 brushes the screen every day. Does it really smell good?
- PHP application docking justswap special development kit【 JustSwap.PHP ]
猜你喜欢

window系统 本机查找端口号占用方法

What knowledge do Python automated testing learn?

Introduction to the structure of PDF417 bar code system

The legality of IPFs / filecoin: protecting personal privacy from disclosure

Take you to learn the new methods in Es5

Description of phpshe SMS plug-in

JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m

事务的本质和死锁的原理

Multi robot market share solution

递归、回溯算法常用数学基础公式
随机推荐
Elasticsearch database | elasticsearch-7.5.0 application construction
消息队列(MessageQueue)-分析
Zero basis to build a web search engine of its own
Introduction to Google software testing
解决 WPF 绑定集合后数据变动界面却不更新的问题
代码重构之法——方法重构分析
Share with Lianyun: is IPFs / filecoin worth investing in?
Ronglian completed US $125 million f round financing
事件监听问题
To Lianyun analysis: why is IPFs / filecoin mining so difficult?
ES6 learning notes (5): easy to understand ES6's built-in extension objects
2020-09-09:裸写算法:两个线程轮流打印数字1-100。
The importance of big data application is reflected in all aspects
Why is the LS command stuck when there are too many files?
CCR coin frying robot: the boss of bitcoin digital currency, what you have to know
2020-08-29:进程线程的区别,除了包含关系之外的一些区别,底层详细信息?
C語言I部落格作業03
嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:王涛
Building a new generation cloud native data lake with iceberg on kubernetes
ES6 learning notes (4): easy to understand the new grammar of ES6