当前位置:网站首页>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]所创,转载请带上原文链接,感谢
边栏推荐
- An article takes you to understand CSS gradient knowledge
- Try to build my mall from scratch (2): use JWT to protect our information security and perfect swagger configuration
- 统计项目代码行数
- Analysis of serilog source code -- how to use it
- What are manufacturing and new automation technologies?
- Elasticsearch database | elasticsearch-7.5.0 application construction
- Contract trading system development | construction of smart contract trading platform
- How does cglib implement multiple agents?
- 【学习】接口测试用例编写和测试关注点
- 如何对数据库账号权限进行精细化管理?
猜你喜欢
随机推荐
python100例項
PHP application docking justswap special development kit【 JustSwap.PHP ]
常用SQL语句总结
Details of dapr implementing distributed stateful service
Why is quicksort so fast?
The importance of big data application is reflected in all aspects
Multi robot market share solution
The method of realizing high SLO on large scale kubernetes cluster
How does filecoin's economic model and future value support the price of fil currency breaking through thousands
Isn't data product just a report? absolutely wrong! There are university questions in this category
DC-1靶機
行为型模式之解释器模式
What are the common problems of DTU connection
Xmppmini project details: step by step from the principle of practical XMPP technology development 4. String decoding secrets and message package
MongoDB与SQL常用语法对应表
An article takes you to understand CSS3 picture border
Introduction to the structure of PDF417 bar code system
大数据处理黑科技:揭秘PB级数仓GaussDB(DWS) 并行计算技术
[efficiency optimization] Nani? Memory overflow again?! It's time to sum up the wave!!
Python basic variable type -- list analysis

