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(&quot;/**") .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(&quot; user&quot;) * .password(&quot; password&quot;) * .roles(&quot; USER&quot;) .and() * .withUser(&quot; admin&quot;) * .password(&quot; password&quot;) * .roles(&quot; USER&quot; , &quot; ADMIN&quot;) ; *}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