The SpringSecurity Permission control framework

1. Permission control

1. Concepts of authentication and authorization

  • Authentication: The function provided by the system to identify users. Usually, providing a user name and password to log in is actually authentication. The purpose of authentication is to let the system know who you are.
  • Authorization: After the user is successfully authenticated, you need to authorize the user, which is to specify the functions that the current user can operate.

What problems have been solved?

  • You must log in to the background management system to enable the function
  • Assign permissions to roles and enable only partial functions for users with incomplete permissions

2. Data model of permission module

  • The users table t_user
  • Role table t_role
  • Permissions table t_permission
  • Role permission relationship table T_ROLE_PERMISSION

RBAC (role-based access Control) associates users with permissions based on roles. In simple terms, a user has several roles, and each role has several permissions. In this way, the user – role – permission authorization model can be constructed. In this model, the relationship between users and roles and between roles and permissions is generally many-to-many.

We call role-based permission control RABC.

What are the roles?

It can be understood as a set of certain number of permissions, the carrier of permissions.

What do permissions look like in an application system?

The operation of function modules, deletion and modification of uploaded files, access to menus, and even a button on the page and visibility control of a picture all belong to the scope of permissions.

The authentication process

Only the user table is needed. When the user logs in, the user table t_user can be queried for verification to determine whether the user name and password entered by the user are correct.

The authorization process

The user can be authorized only after the authentication is complete. You can query the t_role corresponding to the user. Query the corresponding t_permission and resources based on the role.

3. Relationships between tables

  • Roles and roles are many-to-many relationships
  • Roles and permissions are many-to-many
  • Permissions and menus are many-to-many relationships

Second, the Spring Security

1. Introduction

Spring Security is a framework for Security authentication services provided by Spring. Using Spring Security helps simplify the authentication and authorization process.

Liverpoolfc.tv: spring. IO/projects/sp…

English website: www.w3cschool.cn/springsecur…

2. Coordinate

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>5.0.5. RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>5.0.5. RELEASE</version>
</dependency>
Copy the code

3. Summary

  1. SpringSecurity is a security framework in the Spring family that simplifies authentication and authorization in our development.
  2. SpringSecurity encapsulates the Filter internally, requiring only one Filter to be configured in the web.xml container — the proxy Filter. The real Filter is configured in the Spring container
  3. Common Security Framework
    • Spring 的 SpringSecurity
    • The Apache Shirohttp://shiro.apache.org/

4. A sample

demand

Control with Spring Security: Websites (some pages) require login to access (authentication)

steps

  1. Create Maven project spring_security_demo import dependency

     <packaging>war</packaging>
        <properties>
            <spring.version>5.0.5. RELEASE</spring.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>5.0.5. RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>5.0.5. RELEASE</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <configuration>
                        <! -- Specify port -->
                        <port>85</port>
                        <! Request path -->
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    Copy the code
  2. Configure web.xml (front-end controller, SpringSecurity-related filters)

    • DelegatingFilterProxy is used to consolidate third-party frameworks (proxy filters, not real filters, real filters are in the Spring configuration file)
    • The core controller of SpringMVC, in web.xml, mainly configures SpringMVC’s DispathcerServlet and DelegatingFilterProxy proxy filter used to integrate third-party frameworks. The real filter is in the spring configuration file. Used to integrate Spring Security.
    
            
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
             id="WebApp_ID" version="3.0">
    
        <filter>
            <! - 1: DelegatingFilterProxy used to integrate third-party frameworks (proxy filter, filter the real, real filters need to be in the spring configuration file) to integrate the name of the filter must be springSecurityFilterChain when spring Security, Otherwise would be thrown NoSuchBeanDefinitionException exception - >
            <filter-name>springSecurityFilterChain</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/ *</url-pattern>
    </filter-mapping>
    
        <! -- 2: SpringMVC core controller -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <! -- Specify the loaded configuration file with contextConfigLocation -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-security.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
    </web-app>
    Copy the code
  3. Create Spring-security.xml (core)

    • Define which connections can be allowed
    • Defines which connections cannot be allowed, that is, connections that have roles and permissions can be allowed
    • Authentication management: defines login account names and passwords, and grants access roles and permissions

    The interception rules and authentication manager for Spring Security are mainly configured in spring-security.xml.

    
            
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:security="http://www.springframework.org/schema/security"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
         <! Pattern ="/login.html" : "/login.html" : "/login.html" : "/login.html" : "/login.html" -->
        <! --<security:http security="none" pattern="/login.html"></security:http>-->
    
        <! (2) Define which links cannot be allowed (resources must be authenticated to access) and which roles are required. <security: HTTP auto-config="true" use-config ="true"> auto-config="true": Enable automatic configuration. Use-expressions ="true" : specifies which links cannot be allowed to pass through. The intercept-url: specifies which links cannot be allowed to pass through. Use the expression use-expressions="true" access="hasRole('ROLE_ADMIN') : If you do not use the expression use-expressions="false" access="ROLE_ADMIN: Indicates that only roles with ROLE_ADMIN can access system resources.
        <security:http auto-config="true" use-expressions="true">
            <security:intercept-url pattern="/ * *" access="hasRole('ROLE_ADMIN')"></security:intercept-url>
        </security:http>
    
        <! -- ③ Authentication management: define the login account and password, and grant the role or permission to the current user. Security: User name="admin" : login authorities="ROLE_ADMIN" : role (ROLE_ADMIN), permission password="admin" : Password (2) : user name and password, the role of the current user, queried from the database (later) -->
        <security:authentication-manager>
            <security:authentication-provider>
                <security:user-service>
                    <security:user name="admin" authorities="ROLE_ADMIN" password="admin"></security:user>
                </security:user-service>
            </security:authentication-provider>
        </security:authentication-manager>
    </beans>
    Copy the code

    Request URL: http://localhost:85/

    Automatically adjusts to the landing page (provided automatically by springSecurity)

    You need to enter the user name and password (admin/admin) to log in. Spring Security provides a set of security mechanisms that intercept the login

The problem

500: There is no PasswordEncoder mapped for the ID “null”

The solution

The configuration file needs to be modified

<security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"></security:user>
Copy the code

Enter the correct user name and password.

It was found that the error page 404 message was /favicon.ico not found, which is the icon Spring Security automatically points to. But it doesn’t matter. To illustrate another point:

There is no successful login page at this time.

{noop} : indicates that the current password is in plain text. Indicates that the current password does not need to be encrypted with PasswordEncoderFactories.

Under the WebApp folder, create index.html. After login, you can access the index.html normally.

Note:

  1. In the web. The XML configuration inside the permissions related filter, name cannot be changed (springSecurityFilterChain)

    <filter>   
       <filter-name>springSecurityFilterChain</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
       <filter-name>springSecurityFilterChain</filter-name>
       <url-pattern>/ *</url-pattern>
    </filter-mapping>
    Copy the code
  2. No password encryption is specified in the previous example. {noop} is required when configuring the password.

    <security:user-service>
       <security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
    </security:user-service>
    
    Copy the code

5. Example 2

Demand for advanced

The case just mentioned is still not suitable for the real production environment. There are some problems as follows:

  1. In the project, all resources (all requested urls) are protected. In reality, some resources can be accessed without authentication, that is, they can be accessed anonymously. [Resource release, login free access]
  2. The login page is generated by the framework, and our projects tend to use their own login page. [Custom login page]
  3. The user name and password are directly configured in the configuration file, whereas the user name in the real production environment is usually saved in the database. [Login authentication using database data]
  4. The password configured in the configuration file is in plain text, which is insecure. In the real production environment, the password needs to be encrypted. [Password encryption and authentication]

steps

  1. Configuring anonymously accessible resources (resources that can be accessed without login permissions or roles)

    • Create js, CSS directories in your project and provide any test files in both directories

    • Configure in the spring-security.xml file to specify which resources can be accessed anonymously.

      <! -- HTTP: used to define permissions control specifies which resources do not need permissions verification, can use wildcards -->
      <security:http security="none" pattern="/js/**" />
      <security:http security="none" pattern="/css/**" />
      Copy the code
  2. Use the specified login page (login.html)

    • Under the WebApp folder, provide login.html as the login page for your project

      <! DOCTYPEhtml>
      <html>
      <head>
      <meta charset="UTF-8">
          <title>The login</title>
      </head>
      <body>
      <form action="/login.do" method="post">
          username:<input type="text" name="username"><br>
          password:<input type="password" name="password"><br>
          <input type="submit" value="submit">
      </form>
      </body>
      </html>
      
      Copy the code
    • Modify the spring-security.xml file to specify that the login.html page can be accessed anonymously

    • Modify the spring-security. XML file to add the configuration of the form login information

    • Modify the spring-security. XML file to turn off the csrfFilter filter

    
            
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:security="http://www.springframework.org/schema/security"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
         <! Pattern ="/login.html" : "/login.html" : "/login.html" : "/login.html" : "/login.html" -->
        <! --<security:http security="none" pattern="/login.html"></security:http>-->
        <security:http security="none" pattern="/login.html" />
        <! -- HTTP: used to define permissions control specifies which resources do not need permissions verification, can use wildcards -->
        <security:http security="none" pattern="/js/**" />
        <security:http security="none" pattern="/css/**" />
        <! -- form-login: defines the form login information login-page="/login. HTML ": specifies the login page username-parameter="username" : The default value is username password-parameter="password" : indicates the password to use the login name. The default value is password login-processing-url="/login.do" : Default-target-url ="/index.html" : indicates the URL after the login. Authentication-failure-url ="/login. HTML ": /login. HTML always-use-default-target="true" : After a successful login, the system always switches to the address specified by default-target-URL, that is, the default address of the successful login.
        <security:http auto-config="true" use-expressions="true">
            <security:intercept-url pattern="/ * *" access="hasRole('ROLE_ADMIN')"></security:intercept-url>
            <security:form-login login-page="/login.html"
                                 username-parameter="username"
                                 password-parameter="password"
                                 login-processing-url="/login.do"
                                 default-target-url="/index.html"
                                 authentication-failure-url="/login.html"
                                 always-use-default-target="true"/>
            <! -- CSRF: indicates the corresponding CsrfFilter filter. Disabled: indicates whether to enable the CsrfFilter filter. This parameter needs to be disabled if you use a custom login page.
          <security:csrf disabled="true"></security:csrf>
        </security:http>
    Copy the code

    Note:

    Test at this point if the user name and password are entered correctly. Throw an exception:

    403-forbidden

    Analyze the reasons:

    Spring-security adopts the leased-link mechanism, in which the CSRF uses token representation and random characters. Each page accessed will be randomly generated, and then compared with the server. If the page is accessed successfully, it can be accessed, and if the page is not accessed successfully, it cannot be accessed.

    Solution:

    Turn off the leason security request

    <! -- Turn off the security request -->
    <security:csrf disabled="true" />
    
    Copy the code

    What is CSRF?

    CSRF, also known as cross-domain request forgery, allows an attacker to forge user requests to access trusted sites.

    The solution is basically to add some form information that cannot be obtained by attack websites, such as adding image verification codes, which can eliminate CSRF attacks. However, except for login and registration, verification codes are not suitable for other places, because the ease of use of websites is reduced.

  3. Query user information from the database

    • Define the UserService class to realize the UserDetailsService interface.

      public class User {
          private String username;
          private String password;
          private String telephone;
          // Generate set get and toString methods
      }
      Copy the code

      The Map collection is used here to simulate the data extracted from the collection.

      @Component
      public class UserService implements UserDetailsService {
          // Simulate user data in the database
         static Map<String,com.dyy.pojo.User> map =   new HashMap<String,com.dyy.pojo.User>();
      
         static {
             com.dyy.pojo.User user1 =  new com.dyy.pojo.User();
             user1.setUsername("admin");
             user1.setPassword("admin");
             user1.setTelephone("123");
      
             com.dyy.pojo.User user2 =  new com.dyy.pojo.User();
             user2.setUsername("zhangsan");
             user2.setPassword("123");
             user2.setTelephone("321");
      
             map.put(user1.getUsername(),user1);
             map.put(user2.getUsername(),user2);
         }
      
          /** * Load user information based on the user name *@param username
           * @return
           * @throws UsernameNotFoundException
           */
          @Override
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
              System.out.println("username"+username);
              // simulate database query based on user name
              com.dyy.pojo.User userInDb = map.get(username);
      
              if (userInDb==null) {// If no user is found according to the user name, an exception is thrown, indicating that the login name is incorrect
                  return  null;
              }
              // Simulate the database password, later need to query the database
              String passwordInDb ="{noop}" + userInDb.getPassword();
              // Authorization, you need to query the database to dynamically obtain the user's permissions and roles
              List<GrantedAuthority> lists = new ArrayList<>();
              lists.add(new SimpleGrantedAuthority("add"));
              lists.add(new SimpleGrantedAuthority("delete"));
              lists.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
      
              //public User(String username, String password, Collection<? extends GrantedAuthority > authorities)
              // return User,
              // Save the login name,
              (By default, the password obtained from the database is compared with the password obtained on the login page. If the password is displayed on the success page, the login page is displayed on the failure page, and an exception is thrown.)
              // Parameter 3: Stores the permissions of the current user
              return new User(username,passwordInDb,lists);/ / note: framework provides the User class: org. Springframework. Security. Core. The populated userdetails. User}}Copy the code
    • Modify the spring-security.xml configuration (inject UserService)

      <! -- 3: Authentication management, define the login account name and password, and grant access roles and permissions authentication-manager: used to process authentication operations -->
      <security:authentication-manager>
          <! -- authentication-provider: performs the authentication logic -->
          <security:authentication-provider user-service-ref="userService">
          </security:authentication-provider>
      </security:authentication-manager>
      <context:component-scan base-package="com.dyy"/>
      Copy the code

      Register UserService in the Spring configuration file and specify it as the processing class for querying user information by user name during authentication. When we log in, the Spring Security framework calls the loadUserByUsername of UserService to query the user information and compares the database password provided in this method with the form password entered on the user page to implement authentication.

  4. Encrypt passwords

    Common password encryption methods are as follows:

    • 3DES, AES, DES: The symmetric encryption algorithm is used to decrypt the original password
    • MD5 and SHA1: If the HASH algorithm is used, the original password cannot be calculated. However, a rainbow table can be created for table lookup.
    • MD5 can be encrypted with salt to ensure security. The same password value, different salt value, encryption results are different.

    Bcrypt: The salt is randomly mixed into the final encrypted password, and there is no need to provide the previous salt separately during verification, so there is no need to deal with the salt problem separately.

    The BcryptPasswordEncoder method in Spring Security uses SHA-256+ random salt + key symmetric password for encryption. The SHA series are Hash algorithms, not encryption algorithms. Using a decryption algorithm means that decryption can be performed (this is the same as encoding/decoding), but the process is Hash and irreversible.

    (1) Encryption: When registering a user, hash the password entered by the user using SHA-256+ random salt + key to obtain the hash value of the password and store the password in the database.

    (2) Password matching: During user login, password matching is not decrypted (because passwords are irreversibly hashed), but the same algorithm is used to compare passwords entered by users. If they are the same, the entered password is correct.

    ** This is why passwords are processed using hash algorithms rather than encryption algorithms. Even if the database is leaked, it is difficult for hackers to crack the password. ** Two identical passwords may be different after processing, so it is impossible to compare them one by one.

    The encrypted format is generally

    $2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa
    Copy the code

    The encrypted string has a fixed length of 60 characters. Among them

    $is a separator, meaningless;

    2A is the bcrypt encryption version number;

    10 is the value of cos (t);

    The next 22 are salt values;

    The next string is the ciphertext of the password.

    ① Specify the password encryption object in the spring-security. XML file

    <! -- 3: Authentication management, define the login account name and password, and grant access roles and permissions authentication-manager: used to process authentication operations -->
    <security:authentication-manager>
        <! -- authentication-provider: performs the authentication logic -->
        <security:authentication-provider user-service-ref="userService">
            <! -- Specify password encryption policy -->
            <security:password-encoder ref="passwordEncoder"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>
    
    <! -- Configure password encryption object -->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
    
    Copy the code

    ② Modify the UserService implementation class and set the password to the encrypted ciphertext.

  5. Configure multiple verification rules (control permissions on accessed pages)

    ① Modify the spring-security. XML file

    <security: HTTP auto-config= “true” use-config = “true” >

    <security:http auto-config="true" use-expressions="true">
       <! --<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url>-->
        <! -- As long as the authentication is passed, you can access -->
        <security:intercept-url pattern="/index.html"  access="isAuthenticated()" />
        <security:intercept-url pattern="/a.html"  access="isAuthenticated()" />
    
        <! -- Add permission to access b.html pages -->
        <security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />
    
        <! ROLE_ADMIN = ROLE_ADMIN; ROLE_ADMIN = ROLE_ADMIN; ROLE_ADMIN = ROLE_ADMIN
        <security:intercept-url pattern="/c.html"  access="hasRole('ROLE_ADMIN')" />
    
        <! -- ROLE_ADMIN allows you to access the d.html page -->
        <security:intercept-url pattern="/d.html"  access="hasRole('ABC')" />
    </security:http>
    Copy the code
  6. Annotation mode permission control (access to the Controller class permission control)

    In addition to configuring permission verification rules in configuration files, Spring Security can also use annotations to control the invocation of methods in classes. For example, if a method in Controller requires certain permissions to access it, you can use annotations provided by the Spring Security framework to control this.

    ① Configure component scanning and MVC annotation driver in spring-security. XML file for scanning Controller

    <context:component-scan base-package="com.dyy"/>
    <mvc:annotation-driven />
    
    Copy the code

    ② Enable permission annotation support in the spring-security.xml file

    <! Annotations ="enabled" /> <security:global-method-security pre-post-annotations="enabled" />Copy the code

    ③ Create the Controller class and add annotations (@preauthorize) to the Controller methods for permission control

    @RestController
    @RequestMapping("/hello")
    public class HelloController {
    
        @RequestMapping("/add")
        @PreAuthorize("hasAuthority('add')")// indicates that the user must have add permission to invoke the current method
        public String add(a){
            System.out.println("add...");
            return "success";
        }
    
        @RequestMapping("/update")
        @PreAuthorize("hasRole('ROLE_ADMIN')")// Indicates that the user must have the ROLE_ADMIN role to invoke the current method
        public String update(a){
            System.out.println("update...");
            return "success";
        }
    
        @RequestMapping("/delete")
        @PreAuthorize("hasRole('ABC')")// indicates that the user must have an ABC role to invoke the current method
        public String delete(a){
            System.out.println("delete...");
            return "success"; }}Copy the code
  7. Log out

    ①index.html defines the exit login connection

    <! DOCTYPEhtml>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>Login successful!<br>
        <a href="/logout.do">Log out</a>
    </body>
    </html>
    
    Copy the code

    (2) in the spring ws-security. XML

    <! -- logout: logs out of the logout-url: logs out of the request path logout-success-url: Invalidate-session ="true" Invalidate-session ="true" Default is true, Http session is invalid after the user logs out -->
    <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
    
    Copy the code

    According to the above configuration, if the user wants to logout, he only needs to request the URL /logout.do, which invalidates the current session and redirects the login. HTML page.

Third, integrate authority framework

The integration steps

1. Import dependencies

Spring and spring security

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>${spring.security.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>${spring.security.version}</version>
</dependency>
Copy the code

2. Add a filter

Configure the filter DelegatingFilterProxy used to integrate the Spring Security framework in web.xml

<filter>
    <! DelegatingFilterProxy is used to consolidate third-party frameworks (proxy filters, not real filters, Real filters need to be in the spring configuration file) to integrate the name of the filter must be springSecurityFilterChain when spring Security, otherwise you will be thrown NoSuchBeanDefinitionException exception -- -- >
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/ *</url-pattern>
</filter-mapping>
Copy the code

3. Prepare SQL statements

  1. Query user galaxies using login names
  2. Pass the user ID to query the role set
  3. Pass the role ID to query the permission set

4. Prepare Service, Dao interface, and Mapper mapping files

/** * User service interface */
public interface UserService {
    User findUserByUsername(String username);
}

Copy the code
@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public User findUserByUsername(String username) {
        User user = userDao.findUserByUsername(username);
        returnuser; }}Copy the code
/** * Persistent layer Dao interface */
@Repository
public interface UserDao {
    User findUserByUsername(String username);
}

Copy the code

/** * Persistent layer Dao interface */
@Repository
public interface RoleDao {
    Set<Role> findRolesByUserId(Integer userId);
}

Copy the code
/** * Persistent layer Dao interface */
@Repository
public interface PermissionDao {
    Set<Permission> findPermissionsByRoleId(Integer roleId);
}

Copy the code

5. Implement the UserDetailsService interface

@Component
public class SpringSecurityUserService implements UserDetailsService {

    @Reference // Note: The user service is invoked remotely via dubbo
    private UserService userService;

    // Query user information based on the user name
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Remotely invoke the user service to query the user information based on the user name
        com.dyy.pojo.User user = userService.findUserByUsername(username);
        if(user == null) {/ / user name does not exist, an exception is thrown UsernameNotFoundException
            return null;
        }
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
        Set<Role> roles = user.getRoles();
        for(Role role : roles){
            Set<Permission> permissions = role.getPermissions();
            for(Permission permission : permissions){
                / / authorization
                list.add(newSimpleGrantedAuthority(permission.getKeyword())); }}/** * User() * 1: specify the User name * 2: specify the password (SpringSecurity will automatically verify the password) * 3: transfer the granted role and permission */
        UserDetails userDetails = new User(username,user.getPassword(),list);
        returnuserDetails; }}Copy the code

6.springmvc.xml

Because the UserDetailsService is not in the previous package, you need to modify the batch scan package. Because in SpringSecurityUserService loadUserByUsername method need to dubbo remote call names for UserService services.

<! <dubbo:annotation package="com.dyy" />Copy the code

7. Create a spring ws-security. XML

  1. Define which connections can be allowed
  2. Defines which connections cannot be allowed, that is, connections that have roles and permissions can be allowed.
  3. Authentication management, define login account name and password, and grant access roles, permissions.
  4. To enable access to the protected page using the iframe option, you need to add security:frame-options policy=”SAMEORIGIN”.

      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    <! Define which links can be allowed.
    <! -- HTTP: used to define permissions control specifies which resources do not need permissions verification, can use wildcards -->
    <security:http security="none" pattern="/js/**" />
    <security:http security="none" pattern="/css/**" />
    <security:http security="none" pattern="/img/**" />
    <security:http security="none" pattern="/plugins/**" />
    <security:http security="none" pattern="/login.html" />
    <! -- Enable annotation mode permission control -->
    <security:global-method-security pre-post-annotations="enabled" />
    <! HTTP: used to define the related permission control auto-config: If the value is set to true, the framework will provide some default configurations, such as providing a default login page and logging out. If the value is set to false, the framework will display the configuration of providing the login form. Otherwise, an error will be reported. Used to specify whether the access attribute in intercept- URL uses an expression -->
    <security:http auto-config="true" use-expressions="true">

	<security:headers>
    <! -- Set iframe to allow access to protected pages -->
    <security:frame-options policy="SAMEORIGIN"></security:frame-options>
</security:headers>

        <! -- As long as the authentication is passed, you can access -->
        <! -- Intercept-url: define an intercepting rule pattern The default configuration is a comma-delimited list of roles. Only one of these roles is required to access isAuthenticated() : only authenticated (not anonymous) -->
        <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />

        <! -- form-login: define the form login information -->
        <security:form-login login-page="/login.html"
                             username-parameter="username"
                             password-parameter="password"
                             login-processing-url="/login.do"
                             default-target-url="/pages/main.html"
                             authentication-failure-url="/login.html"
                             always-use-default-target="true"
        />

        <! -- CSRF: indicates the corresponding CsrfFilter filter. Disabled: indicates whether to enable the CsrfFilter filter. This parameter needs to be disabled if you use a custom login page.
        <security:csrf disabled="true"></security:csrf>

        <! Logout-url: specifies the request path for logging out of the logout-success-url: specifies the login page -->
        <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
    </security:http>


    <! -- Configure password encryption object -->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />


    <! -- 3: Authentication management, define the login account name and password, and grant access roles and permissions authentication-manager: used to process authentication operations -->
    <security:authentication-manager>
        <! -- authentication-provider: performs the authentication logic -->
        <security:authentication-provider user-service-ref="springSecurityUserService">
            <! -- Specify password encryption policy -->
            <security:password-encoder ref="passwordEncoder"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

</beans>

Copy the code

X-frame-options response header:

X-frame-options HTTP response headers are flags used to indicate to the browser whether to allow a page to be displayed in < Frame >
or . Sites can use this feature to ensure that their content is not embedded in someone else’s site, thus avoiding clickjacking attacks.

Attribute values:

  1. DENY

    Indicates that the page is not allowed to be displayed in frame, even if it is nested in pages of the same domain name.

  2. SAMEORIGIN

    The page can be displayed in the frame of the same domain name page.

  3. ALLOW-FROM uri

    Indicates that the page can be displayed in a frame from the specified source.

8. Springmvc.xml introduces the spring-security.xml file

<import resource="classpath:spring-security.xml"></import>
Copy the code

9. Add permission control annotations to Controller’s methods

@RestController
@RequestMapping("/travelItem")
public class TravelItemController {

    @Reference
    private TravelItemService travelItemService;

    @RequestMapping("/findAll")
    public Result findAll(a){
        List<TravelItem> lists =  travelItemService.findAll();
        return new Result(true,MessageConstant.QUERY_TRAVELITEM_SUCCESS,lists);
    }


    @RequestMapping("/edit")
    @PreAuthorize("hasAuthority('TRAVELITEM_EDIT')")// Check permissions
    public Result edit(@RequestBody TravelItem travelItem){
        travelItemService.edit(travelItem);
        return new Result(true,MessageConstant.EDIT_TRAVELITEM_SUCCESS);
    }

    @RequestMapping("/findById")
    public Result findById(Integer id){
        TravelItem travelItem = travelItemService.findById(id);
        return new Result(true,MessageConstant.QUERY_TRAVELITEM_SUCCESS,travelItem);
    }

    @RequestMapping("/delete")
    @PreAuthorize("hasAuthority('TRAVELITEM_DELETE')")// Verify permissions with TRAVELITEM_DELETE123 test
    public Result delete(Integer id){
        try {
            travelItemService.delete(id);
            return new Result(true,MessageConstant.DELETE_TRAVELITEM_SUCCESS);
        }catch (RuntimeException e){
            return new Result(false,MessageConstant.DELETE_TRAVELITEM_FAIL);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return new Result(false,MessageConstant.DELETE_TRAVELITEM_FAIL);
    }

    @RequestMapping("/findPage")
    @PreAuthorize("hasAuthority('TRAVELITEM_QUERY')")// Check permissions
    public PageResult findPage(@RequestBody QueryPageBean queryPageBean){
        PageResult pageResult =  travelItemService.findPage(queryPageBean.getCurrentPage(),
                queryPageBean.getPageSize(),queryPageBean.getQueryString());
        return pageResult;
    }


    // @requestBody: indicates that the object and JSON data are transferred to each other
    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('TRAVELITEM_ADD')")// Check permissions
    public Result add(@RequestBody TravelItem travelItem){
        try {
            // Add, update, delete, no need to add data to data
            // Query: list objects
            travelItemService.add(travelItem);
            Result result = new Result(true, MessageConstant.ADD_TRAVELITEM_SUCCESS);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Result(false, MessageConstant.ADD_TRAVELITEM_SUCCESS); }}Copy the code

10. Add permission prompt information

  1. Add

    to

    tag

    <! -- Custom exception handling -->
    <security:access-denied-handler ref="customAccessDeniedHandler"/>
    <! - reference customAccessDeniedHandler class to handle the exception, this class is our own definition - >
    Copy the code
  2. AccessDeniedHandler added custom handler classes

    /** * has no access to the processing class */
    @Component
    public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest request,HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException,ServletException {
            if (isAjaxRequest(request)) {// AJAX request, send 403 in response
                Result result = new Result(false."No access"."403");
                String json = JSON.toJSONString(result);
                response.getWriter().print(json);
            } else{// Synchronize request processing
                request.getRequestDispatcher("/pages/error/403.html").forward(request,response); }}/** * Check whether it is an Ajax request */
        public static boolean isAjaxRequest(HttpServletRequest request) {
            if (request.getHeader("accept").indexOf("application/json") > -1
                    || (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest"))) {
                return true;
            }
            return false; }}Copy the code

11. Specify login. HTML as the default login page

Allow login. HTML and define login. HTML as the login page login-page=”/login. HTML “.

<security:http security="none" pattern="/login.html" />
<! -- form-login: define the form login information -->
        <security:form-login login-page="/login.html"
                             username-parameter="username"
                             password-parameter="password"
                             login-processing-url="/login.do"
                             default-target-url="/pages/main.html"
                             authentication-failure-url="/login.html"
                             always-use-default-target="true"
        />

Copy the code

12. The user name is displayed after login

If the login succeeds, import Vue and AXIos to support the sending and receiving of JSON

<script src=".. / js/axios - 0.18.0. Js. ""></script>
Copy the code

Define the username attribute

Use the hook function to call Ajax

<script>
    new Vue({
        el: '#app'.data: {menuList: [].username:null  // Display the user name
        },
	    // Send a request to get the user name of the current logged-in user
        created:function () {           
            // Send a request to get the user name of the current logged-in user
            // Return Result(flag,message,data),data places the User object
            axios.get("/user/getUsername.do").then((response) = >{
                if(response.data.flag){
                    this.username = response.data.data.username;
                }
            }).catch((error) = >{
                this.$message.error("Failed to obtain user name"); }}})); $(function() {
        var wd = 200;
        $(".el-main").css('width', $('body').width() - wd + 'px');
    });
</script>

Copy the code

Modify the index.html page after successful login

<div class="avatar-wrapper">
    <img src=".. /img/user2-160x160.jpg" class="user-avatar">
    <! -- Display user name -->
    {{username}}
</div>
Copy the code

By creating the UserController and providing the getUsername method, Spring Security uses an Authentication object to describe information about the current user. We don’t need to create this Authentication object ourselves. Spring Security will automatically create the corresponding Authentication object for us during our interaction with the system, and then assign the value to the current SecurityContext. * * by * * Authentication. GetPrincipal () can get the information on behalf of the current user.

package com.dyy.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.dyy.constant.MessageConstant;
import com.dyy.entity.PageResult;
import com.dyy.entity.QueryPageBean;
import com.dyy.entity.Result;
import com.dyy.service.UserService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
    @Reference
    private UserService userService;

    // Get the user name of the current login user
    @RequestMapping("/getUsername")
    public Result getUsername(a)throws Exception{
        try{
            User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            return new Result(true, MessageConstant.GET_USERNAME_SUCCESS,user);
        }catch (Exception e){
            return new Result(false, MessageConstant.GET_USERNAME_FAIL); }}}Copy the code

13. The user logs out

Add a hyperlink to log out of the index.html page

<el-dropdown-item divided>
        <span style="display:block;"><a href="/logout.do">exit</a></span>
</el-dropdown-item>
Copy the code

Configure it in the spring-security.xml file

<! Logout-url: request path for logging out of logout-success-url: jump to the login page invalidate-session="true" Destroy the session -->
<security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
Copy the code