This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!
Hi guys, I’m Flower Gie, a serious programmer.
Today, I would like to share with you a project that took Gie two days and two nights without sleep. What is this project? It is a standard project framework integrating SpringBoot, Mybatis, Redis, Shiro, JWT, and Vue.
The whole project does not involve any specific service scenario, and only some common functions are configured, such as: Permissions management, user management, menu management, plus Redis middleware, as well as many useful tools (RedisUtil, Id generator, JWt, etc.), can be said to be ready to use, scalability is very strong, let’s enter the topic, see how to use it.
Introduction and construction of SpringBoot + Mybatis
I don’t know if you have built a project from scratch in the development process, but when Gie first started learning Java, I was struggling with SSM and all kinds of complex configuration files. I was always on the brink of collapse. In recent years, with the widespread use of SpringBoot, a new group of program monkeys are much more comfortable. Because SpringBoot simplifies configuration, development is extremely simple and fast.
1.1 What is SpringBoot
SpringBoot is a new framework developed by the Pivotal team that is designed to simplify the initial setup and development process of a project. It removes a lot of XML configuration files, simplifies complex dependency management, and works with various starter systems to basically automate configuration. If you are using the IDEA development tool, you can complete all the configuration with a few clicks and make it easy to launch.
1.2 Framework Building
There are many ways to build the basic framework. Since it is not the focus of this article, the two most commonly used ways are introduced here. Partners can choose according to their own habits.
- 1. Create a vm using Spring Initializr
Open a browser, type the address start.spring. IO, and you should normally see the following interface.
We can see that there are many configuration items. Here is a brief description of the main configuration items:
-
Project: Choose to create a Project using Maven or Gradle.
-
Language: development Language;
-
Spring Boot: Select the Spring Boot version. The default is the latest version (non-milestone and snapshot version).
-
Project Metadata: Specifies basic information about a Project:
- Group: Generally divided into multiple segments, such as com.basic.business. The first segment is the domain and the second segment is the company name. The domain is divided into org, com, and CN. Org is a non-profit organization, and com is a commercial organization. Take the Apache Tomcat project as an example: the groupId of the project is, its domain is org (Tomcat is a non-profit project), the company name is Apache, and artigactId is Tomcat.
- Artfact: Usually the name of the project or module, which works with the Group to ensure project uniqueness
- Name: indicates the project name
- Description: Indicates the project Description
- Package Name: indicates the Package Name, such as com.huage.base
- Packaging: Indicates the Packaging method
- Java: specifies the JDK version
-
Dependencies: Select the Dependencies you want and it will automatically import them into the generated pom.xml (Maven) or build.gradle (Gradle) when creating the project.
After filling in all the information, click the Generate the Project button, and Spring Initializr will Generate a project that will be downloaded as a ZIP file. After decompressing the package to the local PC, you can import the project using IDEA.
2. Create using IDEA
Click the IDEA menu bar: File -> New -> Project to open the following window:
After selecting the JDK version, click “Next” and you will see the following interface.
Here is where we initialize the dependencies, which can be selected according to the project needs, or added later in the POM file, as well as the SpringBoot version, which is usually the latest stable version.
1.3 Database Design
Because this project does not involve any specific services, only some basic tables are needed, such as SYS_USER, SYS_ROLE, sys_menu, etc.
1.4 standard Version Basic information configuration
After the foundation project is set up, the entire project only has an empty folder and an empty application.properties. At this time, we need to configure the database, dependency packages and Mybatis.
- 1. Configure the file
server.port=18082 spring.application.name=first-program # mysql db spring.datasource.url=jdbc:mysql://localhost:3306/firstProgram?useUnicode=true&characterEncoding=utf-8&allowMultiQueries =true&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis mybatis.type-aliases-package=com.basic.business mybatis.mapper-locations=classpath:mapper/*.xml pagehelper.helper-dialect= mysql pagehelper.reasonable= false pagehelper.support-methods-arguments= true pagehelper.params= count=countsqlCopy the code
- 2. Pom file
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <! <groupId> <groupId> <artifactId>mysql-connector-java</artifactId> </artifactId> The < version > 5.1.40 < / version > < scope > runtime < / scope > < / dependency > <! --druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> < version > 1.1.10 < / version > < / dependency > < the dependency > < groupId > Jakarta. Validation < / groupId > < < artifactId > Jakarta. Validation - API/artifactId > < version > 2.0.2 < / version > < / dependency > < the dependency > <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper-spring-boot-starter.version}</version> </dependency>Copy the code
1.5 Use EasyCode to generate base code quickly
After the above information is configured, we use the EasyCode generator to generate Entity, Dao, Service, Controller and other files. It takes Gie one hour to meet the needs of a week. See how I use EasyCode to complete the god has been described in detail. I’m not going to repeat the wheel here.
2, 【 standard version 】 integrated Swagger
After EasyCode code is generated, Swagger annotations are automatically attached to it. We need a two-step configuration to complete the introduction of Swagger annotations
- Pom depends on
<! </groupId> <artifactId> </artifactId> </artifactId> </artifactId> < version > 1.5.21 < / version > < / dependency > < the dependency > < groupId >. IO springfox < / groupId > < artifactId > springfox swagger - UI < / artifactId > < version > 2.9.2 < / version > < / dependency >Copy the code
- Creating a configuration class
@configuration public class Swagger2Configurate {// Whether to enable swagger@value ("${swagger.enable}") private Boolean enableSwagger; @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .enable(enableSwagger) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build(); } private ApiInfo ApiInfo () {return new ApiInfoBuilder().title(" standard API documentation ").version("1.0").build(); }}Copy the code
Then we start the project, the browser input address, http://127.0.0.1:18082/swagger-ui.html, and see the effect, as the chart, we can see the normal swagger interface, try to call the interface, also can be normal request:
3. [Standard version] Redis integrated
Even if you haven’t used Redis, you often hear about it. In work, Redis is often used. Therefore, in the [standard version] project, Gie also integrated Redis and some related tools, which is very thoughtful.
3.1 What is Redis
In layman’s terms, Redis is a database, running directly in memory, so it runs very fast, and its concurrency is very strong. Redis are in the form of key-value pairs (e.g., “name”:huage) and have five common types of keys:
- String: String
- Hash: dictionary
- List the List:
- Set the Set:
- SortSet: Ordered set
In addition, Redis also has some advanced data structures, such as HyperLogLog, Geo, Pub/Sub, BloomFilter, RedisSearch, etc., which Gie will have a special series to explain.
3.2 Steps for Integrating Redis
- Pom file configuration
<! --redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <! --jedis--> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>Copy the code
- The configuration file
# Redis server address spring.redis. Host =127.0.0.1 # Redis server connection port Spring.redis.port =6379 # Redis server connection password (default: null) spring.redis.password= # Maximum number of connections in the connection pool (negative value indicates no limit) Spring. Redis. Jedis. Pool. Max - active = 1024 # connection pool maximum blocking the wait time (use a negative value indicates no limit) spring. Redis. Jedis. Pool. The Max - wait = 10000 # the maximum idle connections in the connection pool Spring. Redis. Jedis. Pool. Max - idle = 200 # connection pool in minimum free connection spring. Redis. Jedis. Pool. Min - idle = 0 # connection timeout (ms) Spring.redis. timeout=10000 #redis configuration end spring.redis.block-when-exhausted=trueCopy the code
- Initialize the configuration file
Public JedisPool redisPoolFactory() throws Exception {JedisPoolConfig JedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); / / connection runs out whether blocking, false anomaly, true block until timeout, the default true jedisPoolConfig. SetBlockWhenExhausted (blockWhenExhausted); / / whether to enable the pool JMX management functions, the default true jedisPoolConfig. SetJmxEnabled (true); JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password); return jedisPool; }Copy the code
3.3 Code Demonstration
After the above configuration is completed, we only need to use @autowired to introduce RedisTemplate, and then we can easily access Redis. In addition, Gie is used to add a RedisUtil tool class in the project, which includes most of the commands of Redis, enough for daily development and use.
// Redis@autowired private RedisTemplate RedisTemplate; / / will be deposited in the redis name: flower elder brother 】 【 redisTemplate. OpsForValue (). The set (" name ", "flower elder brother"); / / remove the redis redisTemplate. The key is the name of the data in opsForValue () get (" name ");Copy the code
Iv. Introduction of JWT in [standard version
The standard version adopts the front-end and back-end separation architecture. Therefore, in order to maintain the normal communication between the front-end and back-end, the front-end request needs to be accompanied with an identity field token to confirm the validity of the interface request, so JWT is introduced.
4.1 what is JWT
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact, self-contained way to securely transfer information between parties as JSON objects. This information can be verified and trusted because it is digitally signed.
4.2 JWT Application Scenarios
- Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will contain the JWT, allowing the user to access the routes, services, and resources allowed by the token. Single sign-on is a feature of JWT that is now widely used because it has little overhead and can be easily used across domains.
- Information Exchange: JSON Web Tokens is a great way to securely transfer Information between parties. Because JWT can be signed, for example, with public/private key pairs, you can be sure that the sender is who they say they are. Also, because the signature is calculated using headers and payloads, you can verify that the content has not been tampered with.
4.3 Tutorial Introduction
It took Gie to see an article from Zhihu, which is also very well written, and here is a link to show you what JWT is in five minutes
5. [Standard version] Integrate Shiro
Shiro is used for authentication in the baseline version. Firstly, it has been used more in Gie work and I am relatively familiar with it. Second, Shiro is actually easier to understand and the code is easier to read than spring-Security, another similar feature.
5.1 introduction of Shiro
Apache Shiro is a powerful and easy-to-use Java security framework for authentication, authorization, encryption, session management, integration with the Web, caching, and more. These are simply not too hi, a person can liver so many things, can be called the king of liver. Moreover, Shiro is a project under Apache, which gives it a solid footprint, and it’s not tied to any framework or container.
5.2 Shiro Integration steps
The standard version uses Shiro + JWT for authentication, so let’s take a look at how to integrate Shiro into our standard version, regardless of the rationale.
Here need to remind my working experience of the few small driver, meet don’t understand the technology do not scratch the scalp reading concept, hands-on implementation code again, sometimes until the code to run again, watching the results after come back, to see the concept, le le code implementation, will have different feelings, if still feel difficult in study, can come from in the comments section, There are many great gods in the community to answer, do not build behind closed doors.
- Configure the core security transaction manager
@Bean public SecurityManager securityManager() { /** 1. Introduce two types of authentication realm */ /**loginRealm for login authentication, CustomRealm certification for other interface * / securityManager. SetRealms (Lists. NewArrayList (customRealm (), loginRealm ())); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); / * * 2. Set the authentication strategy. * / securityManager setAuthenticator (authenticator ()); /** 3. Close shiro's built-in session. Let every request be filtered */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator()); securityManager.setSubjectDAO(subjectDAO); return securityManager; }Copy the code
To start with [Step 1], we introduced two types of authentication (loginRealm and customRealm), which is very confusing. What is this Realm?
In simple terms, the background will receive requests from the various scenarios, such as login, new user registration, and so on, we can’t slam the door, all with a policy to check all the requests, such as the login request need to invoke a database query login information is correct, and once after the success of the login, send again other interface request, It is obviously not appropriate to connect to the database each time to check whether the user login information is correct, so additional policies are required.
Therefore, the standard version uses two realms. One is the loginRealm that handles login authentication, which only handles authentication login requests and verifies that the login is correct. All other interface requests are intercepted by customRealm.
The setAuthenticator() method is used to set the authentication policy. The setAuthenticator() method is used to set the authentication policy.
/** * multiple realms, Set authentication strategy * / @ Bean public ModularRealmAuthenticator authenticator () {ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator(); / / multiple Realm authentication strategy, the default AtLeastOneSuccessfulStrategy AuthenticationStrategy strategy = new FirstSuccessfulStrategy (); authenticator.setAuthenticationStrategy(strategy); return authenticator; }Copy the code
Is not a face meng, don’t worry, here is a specific scene: Our standard version has two realms for authentication. By default, each of these realms validates a request to our interface. If one of these realms validates and the other fails, then the request failed or failed.
- FirstSuccessfulStrategy: Return only the first Realm that was authenticated successfully and ignore the rest. (Note: “first” here refers to the realm that was successfully certified)
- AtLeastOneSuccessfulStrategy: as long as there is a Realm authentication is successful, unlike FirstSuccessfulSTRATEGY, will return all Realm authentication is successful authentication information;
- AllSuccessfulStrategy: Successful only if all realms have been verified successfully, and returns authentication information for all realms authenticated, failing if any of them fail.
Finally [Step 3], we disable sessions, do not save the user login status, and ensure that each request is re-authenticated.
/** * Disable session and do not save user login status. Guarantee that every request to certification * / @ Bean protected SessionStorageEvaluator SessionStorageEvaluator () {DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator(); sessionStorageEvaluator.setSessionStorageEnabled(false); return sessionStorageEvaluator; }Copy the code
- Custom LoginRealm
LoginRealm is only responsible for handling login requests, and its implementation logic is to get the account password in the request interface, query through the database, and save the query results to Redis for subsequent interface request verification.
/** * query database, Encapsulate the obtained user security data (pseudo-code) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken) authenticationToken) throws AuthenticationException { LoginToken token = (LoginToken) authenticationToken; String username = token.getUsername(); String password = new String((char[]) authenticationToken.getCredentials()); / / query whether there is the current user, the effective user query results can only be 1 SysUser user. = loginService getUserByNameAndPwd (username, password); If (user == null){throw new AuthException(login_pwd_error.getCode (), "Login authentication failed, username or password incorrect "); } redisUtil. Setex (key, value); return new SimpleAuthenticationInfo(jwtEntity, password, getName()); }Copy the code
- Custom CustomRealm
LoginReaml will cache the login information of the successful user. After that, every time the interface calls the background, the token field will be carried in the header request header for authentication, which is performed in the CustomRealm. The false code is as follows:
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1. Get request token CustomerToken jwtToken = (CustomerToken) authenticationToken; . String username = ""; String username = ""; String jwtCache = redisUtil. Get (stringutils. joinWith(SYMBOL_UNDERLINE, REDIS_KEY_PREFIX_LOGIN_ACCOUNT, SYMBOL_UNDERLINE) username), 0); If (jwtCache == null) {log.warn("[failed to obtain password cache, query database account = {}]", username); } else {log.error("[account = {}]", username); } else if (! Stringutils.equals (password, cacheJWtobj.getPassword ())) {log.error(" Cache password verification failed "); } } return new SimpleAuthenticationInfo(jwtObject, jwtToken.getCredentials(), getName()); }Copy the code
- Configuring intercepting Content
Some requests do not need to be intercepted, such as registering interfaces and static resources (CSS, images, JS, etc.), which we need to set in advance in Shiro. The example code is as follows:
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); . / / login address shiroFilterFactoryBean setLoginUrl ("/login "); / / after the success of the login to jump connection shiroFilterFactoryBean setSuccessUrl ("/authorized "); / / unauthorized jump address shiroFilterFactoryBean setUnauthorizedUrl (" / 403 "); / / add custom filters (JwtFilter) Map < String, Filter > filterMap = Maps. NewHashMapWithExpectedSize (1); filterMap.put(JwtFilter.class.getName(), new JwtFilter()); shiroFilterFactoryBean.setFilters(filterMap); FilterChainDefinitionMap <String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/**", JwtFilter.class.getName()); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);Copy the code
- Log on to the test
Here we get the user name and password passed in from the front end, encapsulates it in the LoginToken and calls the Subject. Login () method, which triggers LoginRealm authentication. Why this triggers LoginRealm instead of CustomRealm is described in more detail above.
LoginToken loginToken = new LoginToken(username,password); Subject subject = SecurityUtils.getSubject(); // Login operation (authentication to LoginRealm) subject.login(usernamePasswordToken); // Return the front-end token information JwtEntity JwtEntity = (JwtEntity) Subject.getPrincipal (); return new SysUserLoginDto(JwtUtils.createJwtToken(JSON.toJSONString(jwtEntity)), jwtEntity.getLoginId());Copy the code
As a result of the test, the token value generated by JWT can be obtained. In subsequent requests, the token needs to be carried for authentication:
{"token":"eyJhbGciOiJIUzI1NiJ9.eyJqd3RLZXkiOiJ7XCJhY2NvdW50XCI6XCIxXCIsXCJsb2dpbklkXCI6MSxcInBhc3N3b3JkXCI6XCIweEMwbWZJV GR4MjJ3ejFKMVU1c3pnPT1cIixcInNlc3Npb25JZFwiOlwiODg2MjcyNDkyOTE0NjA2MTJcIn0iLCJleHAiOjE2MjY5MjI5MDJ9.VF8R9X3hZb6SDvShZbRd SRgwaAUUE7dC7XQhIuWBSn4","loginId":1}Copy the code
5. Front-end project construction
The front-end uses version Ant-Design-Vue 2.0, although I have written many front-end projects and used other frameworks, such as elementUi. But for a back-end code farmers, my aesthetic is a little low, usually belong to red with green color…. I won’t go into the process of building the front end in detail here, but there are a lot of great front end leaders in the nuggets community that you can learn from.
conclusion
The length is a bit long, can insist to see here friends are really big guy, but if you still feel obscure after reading, you can directly download the standard version of the open source Gie project, compared with debugging. If you have other ideas, you can also contact Flower Gie. If I happen to know about flowers, I will definitely answer them to my friends. That’s all for today’s sharing.
If you have other good ideas, you can come to Gie’s open source team to study technology and learn together. You are very welcome to join us
Pay attention to avoid getting lost
That’s all for this episode. If you have any mistakes, please leave a comment. Thank you very much. I’m Flower Gie, feel free to leave a comment if you have any questions, and we’ll see you next time at 🦮.
The article continues to be updated. You can read it in the first time on wechat, and you can get interview materials, learning videos, etc. Interested partners are welcome to pay attention, learn together, ha 🐮🥃.
Original is not easy, how can you bear white whore. if you think this article is a little useful to you, thank you for the old iron for this article point a like, comment or forward, because this will be my power to output more quality articles, thank you!