Interested friends can go to understand the first few, your praise is the biggest support for me, thank you!
- The article directories
SpringBoot takeoff road -HelloWorld
(two) SpringBoot takeoff road – analysis of the principle of entry
(three) SpringBoot takeoff road -YAML configuration summary (entry must know will)
(4) SpringBoot takeoff road – static resource processing
(five) SpringBoot takeoff road -Thymeleaf template engine
(6) SpringBoot takeoff road – integration jdbctemplate-druid-mybatis
(7) SpringBoot launch road – integration of SpringSecurity (Mybatis, JDBC, memory)
(eight) SpringBoot take-off path – integration Shiro detailed tutorial (MyBatis, Thymeleaf)
SpringBoot -Swagger 2 And 3
Description:
-
The purpose of this article is to integrate, that is, a specific practical experience, which is not involved in the original reason, and I myself have not in-depth research, so I will not make a fool of myself
-
SpringBoot takeoff road series of articles source, are synchronized to github, there is a need for small partners, go down at will
- Github.com/ideal-20/Sp…
-
Talent and learning, will point to shallow knowledge, we right as a tool to see, do not like not angry ha ~
(I) First acquaintance with Spring Security
(1) introduction
Permissions and security problems, although not a must affect the program, project operation condition, but it is an important factor in the development, for example, we don’t want to be some resources access to or we have some methods to meet the specified identity can access, we can use the AOP or filter to implement requirements, but in fact, If the code involves more logic, the code is extremely tedious, redundant, and many development frameworks, such as Spring Security, Shiro, have provided us with this function, we just need to know how to correctly configure and use it
(2) Basic introduction
Take a look at the introduction of the official website
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a powerful and highly customizable authentication and access control framework. It is the de facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
Spring Security is a framework that focuses on providing authentication and authorization for Java applications. As with all Spring projects, the real power of Spring security is that it can be easily extended to meet custom requirements
In short, Spring Security is a powerful and sophisticated framework for controlling access rights
Web application Security consists of user Authentication and Authorization, which are core features provided by Spring Security
User authentication: User authentication refers to whether the user identity is legitimate. Generally, our user authentication is to judge the validity of the user identity by verifying the user name and password. After confirming the validity of the user identity, the user can access the system
User authorization: If different users have different levels of permissions, user authorization is involved. User authorization controls the resources that users can access and the operations that they can perform. Different permissions are assigned according to different user roles
(2) Static page import And environment construction
(1) About static pages
A: Page introduction
Page is my own temporary, need friends can go to my GitHub: Ideal -20 download source, a brief description of this page
Do a static page if too much trouble, you can simply create some simple pages, write a few title text, can reflect the current page is good
I use these pages in code, is to take the open source front-end component framework for a little beautify, and then convenient to explain some functions, the page template is mainly with Thymeleaf
1. Directory structure
├ ─ ─ index. HTML/ / home page├ ─ ─ images//├ ─ ─ the CSS// The project file can be accessed on the server├ ─ ─ js// Project screenshots├ ─ ─ views// Total subpage folder, the key page for permission verification│ ├ ─ ─ the login. HTML// Make your own login page (instead of the Spring Security default)│ ├ ─ ─ L - A// l-a subpage folder containing A, B, and C subpages│ │ ├ ─ ─ a.h HTML │ │ ├ ─ ─ b.h HTML │ │ ├ ─ ─ c. the TML | ├ ─ ─ L - B// the l-b subpage folder contains a, B and C subpages│ │ ├ ─ ─ a.h HTML │ │ ├ ─ ─ b.h HTML │ │ ├ ─ ─ c. the TML | ├ ─ ─ L - C// l-c subpage folder containing a, B, and C subpages│ │ ├─ a.TML │ ├─ B.html │ ├─ A.htmlCopy the code
B: Import into the project
The main thing is to replace some basic links and introduce something with the tag format of Thymeleaf. The syntax here is not very much, and it is easy to understand even if you are not familiar with Thymeleaf. Of course, if you still feel a little difficult, you can simply make HTML. Or check out my previous post on Thymeleaf for a primer
Go to Resources > static for CSS, image, and JS, and go to Resources > templates for views and index.html
(2) Environment construction
A: Introduce dependencies
It doesn’t matter if this section is imported, or if the project is initialized with auto build checked, as long as it is imported normally
- Introduce the Spring Security module
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code
The key dependencies are primarily the launcher above, but there are others that are general or complementary, such as Web, Thymeleaf, and DevTools
Thymeleaf-extras-springsecurity 5, as discussed later, is used to integrate Spring Security with Thymeleaf
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4. RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Copy the code
B: Page skip Controller
Because we used the template, the jump of the page needs to be handed over to the Controller. It is very simple, the first is the home page, of course, it doesn’t matter about the page, I randomly jump to my blog, and then there is a jump of the login page
There is A small Tip that needs to be mentioned, because l-A, L-B, l-C each have three pages a.HTML, B.HTML, c.HTML, so you can use @pathVariable to write A more general jump method
@Controller
public class PageController {
@RequestMapping({"/", "index"})
public String index(a) {
return "index";
}
@RequestMapping("/about")
public String toAboutPage(a) {
return "redirect:http://www.ideal-20.cn";
}
@RequestMapping("/toLoginPage")
public String toLoginPage(a) {
return "views/login";
}
@RequestMapping("/levelA/{name}")
public String toLevelAPage(@PathVariable("name") String name) {
return "views/L-A/" + name;
}
@RequestMapping("/levelB/{name}")
public String toLevelBPage(@PathVariable("name") String name) {
return "views/L-B/" + name;
}
@RequestMapping("/levelC/{name}")
public String toLevelCPage(@PathVariable("name") String name) {
return "views/L-C/"+ name; }}Copy the code
C: Final effect of environment building
- I made the page a little narrower for the sake of texture
- The upper right corner of the home page should be the login link, because I run the code that has been written, and if I do not log in the page, such as L-A-A and other modules, it cannot be displayed, so I log in with A defined administrator identity
- How to make it automatically switch between displaying login and post-login information will be explained later
1, the first page
2. Sub-pages
In L-A, L-B, L-C, a.HTML, B.HTML, C.HTML are the same, but the text changes A little
3. Login page
(3) Integrate Spring Security (in-memory)
This part, in order to simplify some, easier to understand a few, no embarks from the scene with data (because of the code is less involved, so it’ll explain a little more), but some identity, and so on to write directly to dead, write to the memory, easy to understand, then the next title contains database is given in writing (there will be less interpretation, The main points are just a few differences from the previous one.
(1) Configure the authorization content
A: Source code to understand user authorization
You can go to the official website to have a look. The official website provides us with some samples, including a small sample of configuration class, which is the following one. We can analyze through this example
https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#jc-custom-dsls
@EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(customDsl())
.flag(true)
.and()
...;
}
}
Copy the code
1, create config –> SecurityConfig config config class
- Create a configuration class, as in the website, WebSecurityConfigurerAdapter inheritance
- Add the @enablewebsecurity annotation to the class to EnableWebSecurity mode
- Override the configure(HttpSecurity HTTP) method
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); }}Copy the code
Since it is an override, we can go in and look at the parent class’s source code comment for the configure(HttpSecurity HTTP) method, which has a lot of useful information
The first paragraph says that we want to use HttpSecurity by overriding it, not by calling super, otherwise there will be overrides, and the second paragraph gives a default configuration
* Override this method to configure the {@link HttpSecurity}. Typically subclasses
* should not invoke this method by calling super as it may override their
* configuration. The default configuration is:
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
Copy the code
2, according to the source note analysis
So let’s just write it out in the comments that we just saw, and the first thing we can see is that it supports a chain call
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin()
.and().httpBasic();
}
Copy the code
-
AuthorizeRequests are all about requesting authorization, so asking questions about requesting authorization (allowing a designated user to access a resource with different permissions) requires a call
-
Second, anyRequest().authenticated() means that all HTTP requests need to be authenticated
-
And () connects to some new content, such as a form login or HTTPBasic method (the authentication process here is to ask you to enter your username and password, check your identity, both forms or pop-ups).
Basic is a simple HTTP authentication method. The client transmits the user name and password to the server in plain text (Base64 encoding format) for authentication. Usually, HTTPS is required to ensure the security of information transmission
To show you:
- If no authentication mode is specified
.and().formLogin()
or.and().httpBasic()
Access to any page will prompt 403 forbidden access error
- The specified
.and().formLogin()
Authentication, pop up a form page (built-in, and create no matter)
- The specified
.and().httpBasic();
Authentication, a window pops up for HTTPBasic authentication
B: Customize user authorization
1, first look at the source code comments
The default configuration, set all the HTTP requests require authentication, so we are on a visit to the home page, etc will be blocked, but the actual situation, there are some pages can be anyone to access, such as the home page, or a custom login page, such as, at this moment need some user authorization rules with their definition
In WebSecurityConfigurerAdapter formLogin () near the annotation and saw an interesting content
Note: “stands for quotation marks
* http
* .authorizeRequests(authorizeRequests ->
* authorizeRequests
* .antMatchers("/**") .hasRole(" USER") *)Copy the code
This is what we are looking for, a custom configuration, matching by antMatchers one by one, and specifying its legal identity by hasRole, which means that only users who meet this identity can access the path resources specified above
The ant prefix before Matchers means that he can use an Ant style path expression.
The wildcard | instructions |
---|---|
? | Matches any single character |
* | Matches 0 or any number of characters |
** | Matches 0 or more directories |
Add: If you want to use regular expressions, you can use this method. RegexMatchers ()
Of course, there are many situations where you want to make a path accessible to anyone, such as the home page. The permitAll() method will do just that, but here are some common methods
-
PermitAll () : allows any access
-
DenyAll () : denies all access
-
Anonymous () : Allows anonymous users to access
-
Authenticated () : Allows authenticated users to access
-
HasRole (String) : allows access to/if the user has a given role (user group)
-
HasAnyRole (String)… : Allows access if the user has one of the given roles (user groups).
-
RememberMe () : Access is allowed if the user is authenticated by the Remember-me function
-
FullyAuthenticated () : allows access if the user is fullyAuthenticated(not via the remember-me feature)
-
HasAuthority (String) : Allows access if the user has a given permission
-
HasAnyAuthority (String)… : Allows access if the user has one of the given permissions
-
HasIpAddress (String) : Allows access if the request comes from a given IP address.
-
Not () : Inverts other access results
Note: The difference between hasAnyAuthority(“ROLE_ADMIN”) and hasRole(“ADMIN”) is that hasAnyAuthority(“ROLE_ADMIN”) automatically uses the prefix “ROLE_”
2. Let’s customize user authorization
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/levelA/**").hasRole("vip1")
.antMatchers("/levelB/**").hasRole("vip2")
.antMatchers("/levelC/**").hasRole("vip3")
.and().formLogin();
}
Copy the code
/levelA/ levelB/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC/ levelC
Obviously, although said that defines the content of the authorization, namely what permissions users, access to which resources, but because we did not configure the user information (legal or illegal), so naturally, the login page, in front of is error directly, let’s analyse and how to carry out certification
(2) Configure the authentication content
A: Source code to understand user authentication
In the authorization section, we rewrote the configure(HttpSecurity HTTP) method. Let’s see if there are any methods that might help us authenticate the user. We first look at the way the configure (AuthenticationManagerBuilder auth)
This is one of the examples he uses, and this is actually what we want, as you can see from the comments, to enable username-based authentication in memory
* protected void configure(AuthenticationManagerBuilder auth) {
* auth
* // enable in memory based authentication with a user named
* // " user" and " admin"* .inMemoryAuthentication() * .withUser(" user") * .password(" password") * .roles(" USER") .and() * .withUser(" admin") * .password(" password") * .roles(" USER" , " ADMIN") ; *}Copy the code
Follow suit. We do it first
B: Customized user authentication
The code is as follows:
// Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin")
.password(new BCryptPasswordEncoder().encode("666"))
.roles("vip1"."vip2"."vip3")
.and()
.withUser("ideal-20")
.password(new BCryptPasswordEncoder().encode("666"))
.roles("vip1"."vip2")
.and()
.withUser("jack")
.password(new BCryptPasswordEncoder().encode("666"))
.roles("vip1");
}
Copy the code
We are playing according to the example, but, which we added the coding problem, it requires that coding, otherwise it will report an error, the official recommendation is bcrypt encryption, we use this here, of course, their common MD5 and so on are ok, you can write a tool class
Here, test, in fact, you can according to the identity of the different, thus have access to different path resources your permissions, the main functions have been implemented, add some more friendly functions, such as login and logout button display, and remember the password and so on
(3) Cancellation problem
1. Deregister the configuration
Of course, there are already many configurations, so you can connect via.and(), such as.and().xxx, or write a separate http.xxx as shown below
@Override
protected void configure(HttpSecurity http) throws Exception {...// Unregister the configuration
http.logout().logoutSuccessUrl("/")}Copy the code
Logout () indicates that logout has been enabled, and logoutSuccessUrl(“/”) indicates that the page is logged out and returned to the home page
In the foreground page, I’ve already shown the code for the logout button. This is not fixed, of course, depending on the UI framework and template engine, but the logout path is /logout
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i>The cancellation</a>
Copy the code
(4) Display components according to identity permissions
A: Display of login and logout
There is also such a problem, in the upper right corner, when not logged in, should show the login button, after logged in, should show the user information, and logout, and so on, this part, mainly on the page side of the problem
The display condition is actually very simple, is to judge whether the authentication, the authentication will take out some values, no authentication will show the login
1. At this point, we need to introduce a Thymeleaf with a Spring Security dependency (of course, if it is another technology, it is different).
The address is as follows:
https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4. RELEASE</version>
</dependency>
Copy the code
2. Import the namespace
The purpose of the introduction of this file is to determine the page write permission and other related content can be prompted
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
Copy the code
3. Modify the navigation logic
<! -- Login and logout -->
<div class="right menu">
<! -- If not logged in -->
<div sec:authorize=! "" isAuthenticated()">
<a class="item" th:href="@{/toLoginPage}">
<i class="address card icon"></i>The login</a>
</div>
<! -- If you have logged in -->
<div sec:authorize="isAuthenticated()">
<a class="item">
<i class="address card icon"></i>User name:<span sec:authentication="principal.username"></span>
<! <span SEC :authentication="principal. Authorities "></span>-->
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i>The cancellation</a>
</div>
</div>
Copy the code
B: Display of component panel
The code above, solves the navigation bar problem, but for example in our home page, some sections, for different users to display different
/levelA/, /levelB/, /levelC/; /levelB/; /levelC/
The three panels on our home page are used to display the links of these three pieces. For people without sufficient identity, it is unnecessary to display this panel. Of course, you can choose to display the panel, but what if you want to display the panel according to identity?
Without the specified identity, the panel does not display SEC: Authorize =”hasRole(‘vip1’)”.
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelA/a}">L-A-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/b}">L-A-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/c}">L-A-c</a>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelB/a}">L-B-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/b}">L-B-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/c}">L-B-c</a>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelC/a}">L-A-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/b}">L-C-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/c}">L-C-c</a>
</div>
</div>
</div>
Copy the code
To demonstrate:
(5) Remember the user
If you restart the browser, you need to log in again, for some users, they think it is troublesome, so many websites provide the option to remember the user when logging in
1. A simple configuration will do the trick, in which case the default login page will have a checkbox to remember the user
@Override
protected void configure(HttpSecurity http) throws Exception {...// Remember the user
http.rememberMe();
}
Copy the code
2. But what if the landing page is custom? In fact, you only need to change the configuration to the following.
// Remember my parameters!
http.rememberMe().rememberMeParameter("remember");
Copy the code
Remember above corresponds to the value of the name attribute in input
<input type="checkbox" name="remember"/>
<label>Remember the password</label>
Copy the code
3. What does it do?
You can open the console of the page and have a look. In fact, after the configuration, after the user chooses to remember the password, we will automatically add a cookie called remember-me with an expiration time of 14 days. When you log out, this cookie will be deleted
(6) Customize the login page
1, configuration,
It is true that the login page is ugly, and the lower version is not beautiful. If you want to use your own customized login page, you can join the following configuration
@Override
protected void configure(HttpSecurity http) throws Exception {...// Login form to submit request
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLoginPage")
.loginProcessingUrl("/login")}Copy the code
.loginPage("/toLoginPage")
That is, when you visit some page that requires user authentication, this request will be made to your login page.loginProcessingUrl("/login")
It’s a path in the form to actually submit the request- The other two are a fetch of the username and password that matches the name attribute of the username and password on the page
2. Page hopping
We talked about this a little bit earlier
@RequestMapping("/toLoginPage")
public String toLoginPage(a) {
return "views/login";
}
Copy the code
3. Customize the form submission action Settings for the login page
<form id="login" class="ui fluid form segment" th:action="@{/login}" method="post">.</form>
Copy the code
(7) close CSRF
@Override
protected void configure(HttpSecurity http) throws Exception {...// Disable CSRF: Cross-site request forgery. By default, only post can be used to submit the logout request
http.csrf().disable();
}
Copy the code
(4) Integrate Spring Security (JDBC)
Because the configuration of the user in memory is relatively simple, so some details are also said, based on the above foundation, to see how to use JDBC to implement the above function, of course, this part can only be added, basic will not be used so, the following integration MyBatis is commonly used ()
(1) Create tables and data
Here create three fields: username, password, and role. When inserting data, password is encrypted with MD5 (write a tool class myself).
This is more reasonable. I define the permissions as normal user, normal administrator, super administrator (you can design it yourself).
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL COMMENT 'Username',
`password` varchar(255) DEFAULT NULL COMMENT 'password',
`roles` varchar(255) DEFAULT NULL COMMENT The 'role'.PRIMARY KEY (`uid`)
)
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1.'superadmin'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_SUPER_ADMIN');
INSERT INTO `user` VALUES (2.'admin'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_ADMIN');
INSERT INTO `user` VALUES (3.'ideal-20'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_USER');
Copy the code
(2) Create entities
I used Lombok, but writing my own get set constructor is the same
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer uid;
private String username;
private String password;
private String roles;
}
Copy the code
(3) Configure the authorization content
This part is no different, right
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/levelA/**").hasAnyRole("USER"."ADMIN"."SUPER_ADMIN")
.antMatchers("/levelB/**").hasAnyRole("ADMIN"."SUPER_ADMIN")
.antMatchers("/levelC/**").hasRole("SUPER_ADMIN")
.and().formLogin()
// Login form to submit request
.and().formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLoginPage")
.loginProcessingUrl("/login")
/ / logout
.and().logout().logoutSuccessUrl("/")
/ / remember me
.and().rememberMe().rememberMeParameter("remember")
// Disable CSRF: Cross-site request forgery. By default, only post can be used to submit the logout request
.and().csrf().disable();
}
Copy the code
(4) Configure the authentication content
A: Configure the database
spring:
datasource:
username: root
password: root99
url: jdbc:mysql://localhost:3306/springboot_security_test? serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
server:
port: 8082
Copy the code
B: Specific configuration
In a few places to note:
-
All query statements are queried through username
-
The parameter in the usersByUsernameQuery () method must have a true query result, so I just put a true in the query
-
The MD5 tool class, which I did in a previous project, was salted, and I commented it out because IT was easier when I tested it
-
DataSource DataSource is injected first (select SQL)
// Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,password,true from user where username = ?")
.authoritiesByUsernameQuery("select username,roles from user where username = ?")
.passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return MD5Util.MD5EncodeUtf8((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
returnencodedPassword.equals(MD5Util.MD5EncodeUtf8((String) rawPassword)); }}); }Copy the code
C: MD5 tool class
package cn.ideal.utils;
import java.security.MessageDigest;
/ * * *@ClassName: MD5Util
* @Description: MD5 encryption tool class *@Author: BWH_Steven
* @Date: 2020/4/27 now *@Version: 1.0 * /
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/** * returns uppercase MD5 **@param origin
* @param charsetname
* @return* /
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString.toUpperCase();
}
public static String MD5EncodeUtf8(String origin) {
// origin = origin + PropertiesUtil.getProperty("password.salt", "");
return MD5Encode(origin, "utf-8");
}
private static final String hexDigits[] = {"0"."1"."2"."3"."4"."5"."6"."Seven"."8"."9"."a"."b"."c"."d"."e"."f"};
}
Copy the code
D: Modify the page
At this point, the JDBC integration is successful. As for the previous page, we only need to modify it according to the permissions of our own design. Otherwise, it is the same as before in memory
<div class="ui stackable three column grid">
<div class="column" sec:authorize="hasAnyRole('USER','ADMIN','SUPER_ADMIN')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelA/a}">L-A-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/b}">L-A-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/c}">L-A-c</a>
</div>
</div>
</div>
<div class="column" sec:authorize="hasAnyRole('ADMIN','SUPER_ADMIN')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelB/a}">L-B-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/b}">L-B-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/c}">L-B-c</a>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('SUPER_ADMIN')">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelC/a}">L-C-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/b}">L-C-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/c}">L-C-c</a>
</div>
</div>
</div>
<! -- <div class="column"></div> -->
</div>
Copy the code
(5) Integrate Spring Security (MyBatis)
Because this part of the content is more commonly used, so, I try to give some perfect
(1) Add dependencies
Lombok and Commons-Lang3 are not necessary and can be replaced with native methods, which I’ll cover there
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4. RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Copy the code
(2) create a table
Use the same table as the JDBC section
Three fields: username, password, and role. The password was encrypted by MD5 when inserting data (I wrote a tool class myself).
This is more reasonable. I define the permissions as normal user, normal administrator, super administrator (you can design it yourself).
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL COMMENT 'Username',
`password` varchar(255) DEFAULT NULL COMMENT 'password',
`roles` varchar(255) DEFAULT NULL COMMENT The 'role'.PRIMARY KEY (`uid`)
)
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1.'superadmin'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_SUPER_ADMIN');
INSERT INTO `user` VALUES (2.'admin'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_ADMIN');
INSERT INTO `user` VALUES (3.'ideal-20'.'FAE0B27C451C728867A567E8C1BB4E53'.'ROLE_USER');
Copy the code
(3) the integration of MyBatis
It is a good idea to integrate MyBatis before you configure Spring Security, so that you can only worry about Spring Security
Note: I have simplified this part as much as possible, for example, the connection pool is used by default, if this part of the feeling is still a bit of a problem, you can refer to my previous several articles about integrating MyBatis
A: Configure the database
spring:
datasource:
username: root
password: root99
url: jdbc:mysql://localhost:3306/springboot_security_test? serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
type-aliases-package: cn.ideal.pojo
server:
port: 8081
Copy the code
B: Configure Mapper and XML
UserMapper
@Mapper
public interface UserMapper {
User queryUserByUserName(String username);
}
Copy the code
mapper/UserMapper.xml
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.ideal.mapper.UserMapper">
<select id="queryUserByUserName" parameterType="String" resultType="cn.ideal.pojo.User">
select * from user where username = #{username}
</select>
</mapper>
Copy the code
I’m not going to do the test here, it’s fine
(4) Configure the authorization content
There’s nothing to be said for this part, just like the previous part, which was explained in detail when we explained how to configure the user in memory. Okay
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/levelA/**").hasAnyRole("USER"."ADMIN"."SUPER_ADMIN")
.antMatchers("/levelB/**").hasAnyRole("ADMIN"."SUPER_ADMIN")
.antMatchers("/levelC/**").hasRole("SUPER_ADMIN")
.and().formLogin()
// Login form to submit request
.and().formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLoginPage")
.loginProcessingUrl("/login")
/ / logout
.and().logout().logoutSuccessUrl("/")
/ / remember me
.and().rememberMe().rememberMeParameter("remember")
// Disable CSRF: Cross-site request forgery. By default, only post can be used to submit the logout request
.and().csrf().disable();
}
Copy the code
(5) Configure the authentication content
A: Create UserService
Create a class to implement the UserDetailsService, which is basically a loadUserByname method. In this class, we can inject mapper, etc., to look up the user. If not, we will leave it on the page. The User information is encapsulated in Spring Security’s own User class, and Spring Security compares the foreground data to it to perform actions, such as authentication success or errors
Note:
- StringUtils is in Commons. Lang3, we use a void detection function, if you don’t want to use it, use native is the same reason, that’s not the point
- Be careful to distinguish between your own User and Spring Security’s User
@Service
public class UserService<T extends User> implements UserDetailsService{
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.queryUserByUserName(username);
if (username == null) {throw new UsernameNotFoundException("User name does not exist");
}
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
String role = user.getRoles();
if (StringUtils.isNotBlank(role)){
authorityList.add(new SimpleGrantedAuthority(role.trim()));
}
return neworg.springframework.security.core.userdetails .User(user.getUsername(),user.getPassword(),authorityList); }}Copy the code
B: Modify the configuration class
This is also very familiar, we can call userDetailsService, we also need to specify the encoding related content to instantiate PasswordEncoder, we need to override encode, matches
// Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return MD5Util.MD5EncodeUtf8((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
returnencodedPassword.equals(MD5Util.MD5EncodeUtf8((String) rawPassword)); }}); }Copy the code
C: Adds the MD5 tool class
In fact, it has been given above, but in case you look inconvenient, here is another paste
The MD5 tool class, which I did in a previous project, was salted, and I commented it out because I could make it easier when I tested it
package cn.ideal.utils;
import java.security.MessageDigest;
/ * * *@ClassName: MD5Util
* @Description: MD5 encryption tool class *@Author: BWH_Steven
* @Date: 2020/4/27 now *@Version: 1.0 * /
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/** * returns uppercase MD5 **@param origin
* @param charsetname
* @return* /
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString.toUpperCase();
}
public static String MD5EncodeUtf8(String origin) {
// origin = origin + PropertiesUtil.getProperty("password.salt", "");
return MD5Encode(origin, "utf-8");
}
private static final String hexDigits[] = {"0"."1"."2"."3"."4"."5"."6"."Seven"."8"."9"."a"."b"."c"."d"."e"."f"};
}
Copy the code
(6) Conclusion
If there is any deficiency in the article, you are welcome to leave a message to exchange, thank friends for their support!
If it helps you, follow me! If you prefer the way of reading articles on wechat, you can follow my official account
We don’t know each other here, but we are working hard for our dreams
A adhere to push original development of technical articles of the public number: ideal more than two days