Spring Security: Basics

1. Getting to know Spring Security first

1.1 Spring Security concept

  Spring Security is a security framework implemented by Spring using AOP thinking and based on servlet filters. It provides a complete authentication mechanism and method-level authorization functions. It is a very good permission management framework.

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 dedicated to providing authentication and authorization for Java applications. Like all Spring projects, the real power of Spring Security lies in its ability to be easily extended to meet custom needs.

feature

  • Comprehensive and extensible support for authentication and authorization
  • Protection against session fixation, clickjacking, cross-site request forgery, and more
  • Servlet API integration
  • Optional integration with Spring Web MVC

1.2 Quick Start Case

1.2.1 Environment preparation

We prepare a SpringMVC+Spring+jsp Web environment, and then integrate SpringSecurity on this basis.

Create a Web project first

Add related dependencies

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>
copy

Add relevant configuration files

Spring configuration file

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
​
    <context:component-scan base-package="com.xxx.service" ></context:component-scan>
​
</beans>
copy

SpringMVC configuration file

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
​
    <context:component-scan base-package="com.xxx.controller"></context:component-scan>
​
    <mvc:annotation-driven ></mvc:annotation-driven>
​
​
</beans>
copy

log4j.properties file

log4j.rootCategory=INFO, stdout
​
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
​
copy

web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
​
<web-app version="2.5" id="WebApp_ID" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>Archetype Created Web Application</display-name>
​
  <!-- initialization spring container -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
​
  <!-- post Garbled filter -->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- front controller -->
  <servlet>
    <servlet-name>dispatcherServletb</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- contextConfigLocation Not required, if not configured contextConfigLocation, springmvc The configuration file defaults to: WEB-INF/servlet of name+"-servlet.xml" -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServletb</servlet-name>
    <!-- intercept all requests jsp except -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
​
</web-app>
​
copy

Add the Tomcat plugin to start the test

    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8082</port>
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>
copy

1.2.2 Integrating Spring Security

Add related dependencies

spring-security-core.jar core package, any SpringSecurity function requires this package

spring-security-web.jar: necessary for web engineering, including filters and related web security infrastructure codes

spring-security-config.jar: for xml file parsing and processing

spring-security-tablibs.jar: dynamic tag library

<!-- Add to SpringSecurity related dependencies -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>
copy

Configure Spring Security in the web.xml file

  <!-- Configure the filter chain springSecurityFilterChain fixed name -->
  <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

Add Spring Security configuration file

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- SpringSecurity configuration file -->
    <!--
        auto-config:Indicates automatic loading SpringSecurity configuration file
        use-expressions: means to use Spring of EL expression
     -->
    <security:http auto-config="true" use-expressions="true">
        <!--
            Intercept resources
            pattern="/**" block all resources
            access="hasAnyRole('ROLE_USER')" means only ROLE_USER This role can access resources
         -->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')" ></security:intercept-url>
    </security:http>
    <!-- Authentication user information -->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service >
                <!-- set up an account zhangsan Password 123 {noop} Indicates no encryption and has a role of  ROLE_USER-->
                <security:user name="zhangsan" authorities="ROLE_USER" password="{noop}123" ></security:user>
                <security:user name="lisi" authorities="ROLE_USER" password="{noop}123456" ></security:user>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>
copy

Introduce SpringSecurity configuration files into Spring

Start test access

2. Authentication operation

2.1 Customize the login page

How to use the login page we wrote ourselves?

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>log in page</h1>
    <form action="/login" method="post">
        account:<input type="text" name="username"><br>
        password:<input type="password" name="password"><br>
        <input type="submit" value="Log in">
    </form>
</body>
</html>
​
copy

Modify related configuration files

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- SpringSecurity configuration file -->
    <!--
        auto-config:Indicates automatic loading SpringSecurity configuration file
        use-expressions: means to use Spring of EL expression
     -->
    <security:http auto-config="true" use-expressions="true">
        <!-- Access the login page anonymously-->
        <security:intercept-url pattern="/login.jsp" access="permitAll()"/>
        <!--
            Intercept resources
            pattern="/**" block all resources
            access="hasAnyRole('ROLE_USER')" means only ROLE_USER This role can access resources
         -->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')" />
​
        <!--
            Configure authentication information
        -->
        <security:form-login login-page="/login.jsp"
                             login-processing-url="/login"
                             default-target-url="/home.jsp"
                             authentication-failure-url="/error.jsp"
        />
        <!-- log out -->
        <security:logout logout-url="/logout"
                         logout-success-url="/login.jsp" />
    </security:http>
    <!-- Authentication user information -->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service >
                <!-- set up an account zhangsan Password 123 {noop} Indicates no encryption and has a role of  ROLE_USER-->
                <security:user name="zhangsan" authorities="ROLE_USER" password="{noop}123" ></security:user>
                <security:user name="lisi" authorities="ROLE_USER" password="{noop}123456" ></security:user>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>
copy

After visiting the home.jsp page, it will automatically jump to the custom login page, indicating that this requirement is realized

But when we submitted the request, the following error occurred on the page

2.2 Turn off CSRF interception

Why is there no problem with CRSF interception in the system's default login page submission?

What should I do if my customized authentication page does not have this information? Two ways:

Disable CSRF interception

Login successfully~

Use CSRF protection

Add the corresponding taglib to the page

We visit the login page

login successful

2.3 Database Authentication

In the previous case, our account information is directly written in the configuration file, which is obviously not good. Let’s introduce how to implement the authentication with the information in the database

Add related dependencies

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.4</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.4</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.11</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.8</version>
    </dependency>
copy

add configuration file

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/logistics?characterEncoding=utf-8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
copy
<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
​
    <context:component-scan base-package="com.bobo.service" ></context:component-scan>
​
    <!-- SpringSecurity configuration file -->
    <import resource="classpath:spring-security.xml" />
​
    <context:property-placeholder location="classpath:db.properties" />
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
     </bean>
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactoryBean" >
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <property name="mapperLocations" value="classpath:mapper/*.xml" />
    </bean>
​
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xxx.mapper" />
    </bean>
</beans>
copy

The service that needs to complete the authentication inherits the UserDetailsService parent interface

Implement the verification method in the implementation class

package com.xxx.service.impl;
​
import com.xxx.mapper.UserMapper;
import com.xxx.pojo.User;
import com.xxx.pojo.UserExample;
import com.xxx.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
​
import java.util.ArrayList;
import java.util.List;
​
@Service
public class UserServiceImpl implements IUserService {
​
    @Autowired
    private UserMapper mapper;
​
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        // Query user information based on account
        UserExample example = new UserExample();
        example.createCriteria().andUserNameEqualTo(s);
        List<User> users = mapper.selectByExample(example);
        if(users != null && users.size() > 0){
            User user = users.get(0);
            if(user != null){
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                // Set the role of the login account
                authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
                UserDetails userDetails = new org.springframework.security.core.userdetails.User(
                        user.getUserName(),"{noop}"+user.getPassword(),authorities
                );
                return userDetails;
            }
        }
        return null;
    }
}
​
copy

Finally, modify the configuration file to associate our custom service

2.4 Encryption

The encryption algorithm recommended in Spring Security is BCryptPasswordEncoder

First generate secret

Modify the configuration file

remove {noop}

2.5 Certification Status

The status of the user includes whether it is available, the account expires, the certificate expires, the account is locked, and so on.

We can add related fields in the user's table structure to maintain this relationship

2.6 remember me

Add a remember me button to the form page.

The RememberMe function is turned off by default in Spring Security, we need to let it go

This completes the configuration.

The function of remembering me will be convenient for everyone to use, but the security is worrying, because the Cookie information stored on the client is easy to be stolen, at this time we can persist these data to the database.

CREATE TABLE `persistent_logins` (
    `username` VARCHAR (64) NOT NULL,
    `series` VARCHAR (64) NOT NULL,
    `token` VARCHAR (64) NOT NULL,
    `last_used` TIMESTAMP NOT NULL,
    PRIMARY KEY (`series`)
) ENGINE = INNODB DEFAULT CHARSET = utf8
​
copy

3. Authorization

3.1 Annotation use

Enable annotation support

<?xml version="1.0" encoding="UTF-8"?>
<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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">
​
    <context:component-scan base-package="com.xxx.controller"></context:component-scan>
​
    <mvc:annotation-driven ></mvc:annotation-driven>
​
    <!--
        Enable permission control annotation support
        jsr250-annotations="enabled" He expressed support jsr250-api Annotation support requires jsr250-api of jar Bag
        pre-post-annotations="enabled" He expressed support Spring Expression annotations for
        secured-annotations="enabled" this is SpringSecurity Comments provided
     -->
    <security:global-method-security
        jsr250-annotations="enabled"
        pre-post-annotations="enabled"
        secured-annotations="enabled"
    />
</beans>
copy

The use of jsr250

add dependencies

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>jsr250-api</artifactId>
    <version>1.0</version>
</dependency>
copy

Set via annotations in the controller

package com.xxx.controller;
​
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
​
import javax.annotation.security.RolesAllowed;
​
@Controller
@RequestMapping("/user")
public class UserController {
​
    @RolesAllowed(value = {"ROLE_ADMIN"})
    @RequestMapping("/query")
    public String query(){
        System.out.println("user query....");
        return "/home.jsp";
    }
    @RolesAllowed(value = {"ROLE_USER"})
    @RequestMapping("/save")
    public String save(){
        System.out.println("user added....");
        return "/home.jsp";
    }
​
    @RequestMapping("/update")
    public String update(){
        System.out.println("user update....");
        return "/home.jsp";
    }
}
​
copy

Use of Spring expressions

package com.xxx.controller;
​
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
​
import javax.annotation.security.RolesAllowed;
​
@Controller
@RequestMapping("/order")
public class OrderController {
​
    @PreAuthorize(value = "hasAnyRole('ROLE_USER')")
    @RequestMapping("/query")
    public String query(){
        System.out.println("user query....");
        return "/home.jsp";
    }
    @PreAuthorize(value = "hasAnyRole('ROLE_ADMIN')")
    @RequestMapping("/save")
    public String save(){
        System.out.println("user added....");
        return "/home.jsp";
    }
​
    @RequestMapping("/update")
    public String update(){
        System.out.println("user update....");
        return "/home.jsp";
    }
}
​
copy

Annotations provided by Spring Security

package com.xxx.controller;
​
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
​
@Controller
@RequestMapping("/role")
public class RoleController {
​
    @Secured("ROLE_USER")
    @RequestMapping("/query")
    public String query(){
        System.out.println("user query....");
        return "/home.jsp";
    }
​
    @Secured("ROLE_ADMIN")
    @RequestMapping("/save")
    public String save(){
        System.out.println("user added....");
        return "/home.jsp";
    }
​
    @RequestMapping("/update")
    public String update(){
        System.out.println("user update....");
        return "/home.jsp";
    }
}
​
copy

exception handling

Add an error page and configure it in the SpringSecurity configuration file

Of course, you can also use the various exception handlers in SpringMVC introduced earlier

3.2 Label usage

The permission management of annotations described above can control whether the user has the permission of this operation, but when the user has this permission and enters the specific operation page, we still need to carry out finer-grained control. At this time, the way of annotation is Not very applicable, at this time we can deal with it through labels

Add SpringSecurity tag library

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>welcome...</h1>
    <security:authentication property="principal.username" />
    <security:authorize access="hasAnyRole('ROLE_USER')" >
        <a href="#">User Query</a><br>
    </security:authorize>
    <security:authorize access="hasAnyRole('ROLE_ADMIN')" >
        <a href="#">User Add</a><br>
    </security:authorize>
    <security:authorize access="hasAnyRole('ROLE_USER')" >
        <a href="#">User Update</a><br>
    </security:authorize>
    <security:authorize access="hasAnyRole('ROLE_ADMIN')" >
        <a href="#">User Delete</a><br>
    </security:authorize>
</body>
</html>
​
copy

page effect

Tags: Java Spring xml Spring Security jar

Posted by msnhockey on Wed, 30 Nov 2022 20:01:39 +0300