SpringSecurity permission control ✧ (intermediate level)

( • ̀ ω •́ ) ✧ spring security intermediate

1, Expression based access control ⭐

access() can be used to realize the same functions as the permission control learned before.. antMatchers.access("expression or method") has a wide range of expressions, such as:. anyRequest.access(@ class name. Method): determine where to access through the url, and determine whether there is permission in combination with the obtained permission set (suitable for project development).

Custom method Demo:

1. Custom method

① Write SecurityAccess.java for the service layer

@Service
public class SecurityAccess {
	//Custom method
    public boolean authority(HttpServletRequest request, Authentication authentication){
        //Get permission list
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        if(!CollectionUtils.isEmpty(authorities)){
            //Get uri
            String uri=request.getRequestURI();
            //Get permission code through uri
            SimpleGrantedAuthority simple=new SimpleGrantedAuthority(uri);
            if(authorities.contains(simple)){
                return true;
            }
        }
        return false;
    }
}
2. Modify the configuration file SpringSecurityConfig.java
@SpringBootConfiguration
/*Expression and method permission control*/
public class SpringSecurityConfig4 extends WebSecurityConfigurerAdapter {
    //Introduce handle
    @Autowired
    SecuritySuccessHandle securitySuccessHandle;
    @Autowired
    SecurityFailHandle securityFailHandle;
    @Autowired
    SecurityAccessDeniedHandler securityAccessDeniedHandler;
    @Autowired
    SecurityAccess securityAccess;

    //Configure the implementation class of passwordEncode
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    //Override method configuration
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Turn off csrf
        http.csrf().disable();
        //Configure login page
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                //Login succeeded handle
                .successHandler(securitySuccessHandle)//Post request
                //Login failed handle
                .failureHandler(securityFailHandle)
                //Configure login parameters
                .usernameParameter("userName")
                .passwordParameter("passWord");
        //Permission configuration
        http.authorizeRequests()

                //Please allow access
                .antMatchers("/login.html","/login").permitAll()
                //The resource is not accessed by anyone
                .antMatchers("/denyall.html").denyAll()
                //access() judged by Uri
                .anyRequest().access("@securityAccess.authority(request,authentication)");

//        Handling of 403 cases
                http.exceptionHandling()
                        .accessDeniedHandler(securityAccessDeniedHandler);

    }
}
3. Configure user permissions
@Service
/*Custom user*/
public class SpringSecurityService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //Judge user name
        if(StringUtils.isEmpty(s)||!("admin".equals(s))){
            throw new UsernameNotFoundException("This user does not exist!");
        }
        //Password encryption
        String pw=passwordEncoder.encode("123456");
        //Return a user
        return new User(s,pw, AuthorityUtils.commaSeparatedStringToAuthorityList("/tomain"));
    }
}
//=========================================================
//The Controller sets a process for accessing / tomain
@GetMapping("/tomain")
    public String main(){
        return "Welcome to login";
    }

Through the above configuration, it can be concluded that only / tomain can be accessed. The following is the test


2, Annotation based access control ⭐

  • First, you need to add comments on startup: @ EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true), where @ Secured: is specifically used to judge whether it has a ROLE. It can be written on a method or class. The parameters should start with ROLE. Second, both @ PreAuthorize and @ PostAuthorize are method or class level comments. Here, @ PreAuthorize is resolved
1. @ Secured and @ PreAuthorize small cases

Springsecurityconfig5. Java file:

@SpringBootConfiguration
/*Annotation permission control*/
public class SpringSecurityConfig5 extends WebSecurityConfigurerAdapter {
    //Introduce handle
    @Autowired
    SecuritySuccessHandle securitySuccessHandle;
    @Autowired
    SecurityFailHandle securityFailHandle;
    @Autowired
    SecurityAccessDeniedHandler securityAccessDeniedHandler;

    //Configure the implementation class of passwordEncode
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    //Override method configuration
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Turn off csrf
        http.csrf().disable();
        //Configure login page
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                //Login succeeded handle
                .successHandler(securitySuccessHandle)//Post request
                //Login failed handle
                .failureHandler(securityFailHandle)
                //Configure login parameters
                .usernameParameter("userName")
                .passwordParameter("passWord");

        //Permission configuration
        http.authorizeRequests()
                //Please allow access
                .antMatchers("/login.html","/login").permitAll()
                //The resource is not accessed by anyone
                .antMatchers("/denyall.html").denyAll()
                //Configure other resources. You must be logged in to access them
                .anyRequest().authenticated();//Put last

                //Handling of 403 cases
                http.exceptionHandling()
                        .accessDeniedHandler(securityAccessDeniedHandler);

    }
}
//Nothing has changed

MainController .java:

@RestController
public class MainController {

    @GetMapping("/tomain")
    public String main(){
        return "Welcome to login";
    }
    @PostMapping("/toagain")
    public String toagain(){
        return "Login failed, please try again<a href=\"/login.html\">Sign in</a>";
    }
/*Annotation role access*/
    @Secured({"ROLE_PP"})
    @GetMapping("zj")
    public String tozj(){
        return "Annotation permission access!";
    }
/*Annotation permission code access*/
    @PreAuthorize("hasAuthority('1000')")
    @GetMapping("ma")
    public String toma(){
        return "Annotation permission code access!";
    }

Finally, add @ EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) to the startup class

The above directly sets the annotation role control and permission code control, / zj and / ma can only be accessed by users whose role is ROLE_PP and permission code is 1000. The user permissions are set as follows:



Next level 🏓

3, Implementation of login free memberme function

  • Introducing dependency mysql,mybatis(jdbc)
  • Configure data source application.yml
  • Configure persistence layer and designated user login free
1. Introduce dependency
 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
    <!-- lookup parent from repository -->
  </parent>
  
  <dependencies>
    <!--spring security assembly-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--web assembly-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- test assembly-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-test</artifactId>
      <scope>test</scope>
    </dependency>

    <!-- mybatis rely on -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.1</version>
    </dependency>
    <!-- mysql Database dependency -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>
  </dependencies>
  
   <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
2. Configure data sources

application.yml:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

3. Login free specific configuration remmberconfig. Java

Remember to comment out the table creation statement when re executing to avoid creating the table for the second time

/*Configure persistence layer objects*/
@Configuration
    public class RemmberConfig {
        @Autowired
        private DataSource dataSource;
        @Bean
        public PersistentTokenRepository getPersistentTokenRepository(){
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            jdbcTokenRepository.setDataSource(dataSource);
            //Automatic table creation is required for the first startup and commented out for the second startup
            jdbcTokenRepository.setCreateTableOnStartup(true);
            //Returns a persistence layer object
            return jdbcTokenRepository;
        }
    }
5. Configuration and static page addition

SpringSecurityConfig.java

// Remember me
        http.rememberMe()
                //Failure time in seconds
                .tokenValiditySeconds(60*60*24*7)
                //Which object does the login logic give to
                .userDetailsService(springSecurityService)
                // Persistent layer object
                .tokenRepository(remmberConfig.getPersistentTokenRepository());

login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    user name:<input type="text" name="userName" /><br/>
    password:<input type="password" name="passWord" /><br/>
    <input type="checkbox" name="remember-me" value="true"/><br/>
    <input type="submit" value="Sign in" />
</form>
</body>
</html>


This means success. You don't have to log in next time

4, Integration of spring security in Thymeleaf

1. Introduce dependency
 <!--thymeleaf springsecurity5 rely on-->
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
    <!--thymeleaf rely on-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

Bring in namespace:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" >
2. Small Demo implementation


Create a new Templates folder in project resources and a new demo.html page in templates

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras- springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Login account:<span sec:authentication="name"></span><br/>
Login user name:<span sec:authentication="principal.username"></span><br/>
Voucher:<span sec:authentication="credentials"></span><br/>
Permissions and roles:<span sec:authentication="authorities"></span><br/>
Client address:<span sec:authentication="details.remoteAddress"></span> <br/>
sessionId: <span sec:authentication="details.sessionId"></span><br/>

Judging by authority:
<button sec:authorize="hasAuthority('/insert')">newly added</button>
<button sec:authorize="hasAuthority('/delete')">delete</button>
<button sec:authorize="hasAuthority('/update')">modify</button>
<button sec:authorize="hasAuthority('/select')">see</button> <br/>
Judging by role:
<button sec:authorize="hasRole('a')">newly added</button>
<button sec:authorize="hasRole('b')">delete</button>
<button sec:authorize="hasRole('c')">modify</button>
<button sec:authorize="hasRole('d')">see</button>
<a href="/logout">Log out</a>
</body>
</html>

Write the Controller layer to jump the page

@Controller
public class TestController {
    /*thyemleaf*/
    @RequestMapping("demo")
    public String todemo(){
        return "demo";
    }
}

Set role permissions. thymeleaf displays different contents through different permissions of roles!

 //Return a user
 return new User(s,pw, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_b,ROLE_a,/insert ,/delete"));

result:

Summary: it can be seen from the above that only the addition, deletion and user information are displayed through different roles and allowed URIs. These are not displayed through modification. Different permissions and different contents are realized through view settings

5, Log out

It's very simple. You just need to send a / loginOut request to the project. Just add a hyperlink to / logout in the page. Implement it below (add hyperlink).

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras- springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Login account:<span sec:authentication="name"></span><br/>
Login user name:<span sec:authentication="principal.username"></span><br/>
Voucher:<span sec:authentication="credentials"></span><br/>
Permissions and roles:<span sec:authentication="authorities"></span><br/>
Client address:<span sec:authentication="details.remoteAddress"></span> <br/>
sessionId: <span sec:authentication="details.sessionId"></span><br/>

Judging by authority:
<button sec:authorize="hasAuthority('/insert')">newly added</button>
<button sec:authorize="hasAuthority('/delete')">delete</button>
<button sec:authorize="hasAuthority('/update')">modify</button>
<button sec:authorize="hasAuthority('/select')">see</button> <br/>
Judging by role:
<button sec:authorize="hasRole('a')">newly added</button>
<button sec:authorize="hasRole('b')">delete</button>
<button sec:authorize="hasRole('c')">modify</button>
<button sec:authorize="hasRole('d')">see</button>
<a href="/logout">Log out</a>
</body>
</html>

Add SpringSecurityConfig.java

http.logout()
                //Exit access path
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login.html");

6, CSRF in spring security

1. What is CSRF?
  • CSRF (Cross Site Request Forgery) cross site request forgery, also known as "OneClick Attack" or
  • Session Riding. An illegal request to access a trusted site by forging a user's request.
    Cross domain: as long as the network protocol, ip address and port are different, it is a cross domain request.
  • When the client interacts with the service, because the http protocol itself is a stateless protocol, a cookie is introduced to record the client's identity. The session id is stored in the cookie to identify the client's identity. In the case of cross domain, the session id may be maliciously hijacked by a third party. When a request is made to the server through this session id, the server will consider the request It is legal and many unexpected things may happen.
2. Small Demo case

The front-end page uses the thymeleaf template and creates new templates/login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/>
    user name:<input type="text" name="userName" /><br/>
    password:<input type="password" name="passWord" /><br/>
    <input type="submit" value="Sign in" />
</form>
</body>
</html>

Jump when the Controller layer accesses showLogin:

/*showLogin Jump*/
    @RequestMapping("showLogin")
    public String tologin(){
        return "login";
    }

Remember to comment out the following in ServiecSecurityConfig: / / http.csrf().disable(); test results:

In this way, the token. Pass token is obtained

7, Oauth2 certification ⭐

Introduction: the third-party authentication technical scheme mainly solves the general standard problem of authentication protocol, because in order to realize cross system authentication, certain interface protocols must be followed between systems. OAUTH protocol provides a safe, open and simple standard for user resource authorization, which greatly saves programmer's time and provides convenience and efficiency.

1. Common language

2. Characteristics
  • advantage:
    More secure, the client does not contact the user password, and the server is easier to be centrally protected
    Widely disseminated and continuously adopted
    Short life and encapsulated token
    Resource server and authorization server are decoupled
    Centralized authorization, simplified client
    HTTP/JSON is friendly and easy to request and pass token s
    Consider a variety of client architecture scenarios
    Customers can have different levels of trust
  • Disadvantages:
    The protocol framework is too broad, resulting in poor compatibility and interoperability of various implementations
    It's not an authentication protocol. It doesn't tell you any user information by itself.
3. Authorization mode
  • Authorization code mode
  • Password mode
① . authorization code mode ⭐

The user logs in and accesses the specified authorization server by configuring the user. The authorization server will return an authorization code to the user. At this time, the user takes the authorization code and accesses oauth/token to exchange the token. After obtaining the token, he accesses the resource server again and takes the resource.

(1) Introduction of dependency

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
    <!-- lookup parent from repository -->
  </parent>
  
<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

	  <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>

(2) Write entity class User.java

public class User implements UserDetails {

    private String username;
    private String password;
    private List<GrantedAuthority> authorities;

    public User(String username, String password, List<GrantedAuthority> authorities) {
        this.username = username;
        this.password = password;
        this.authorities = authorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

(3) Write UserService.java
Configure user account and password

@Service
public class UserService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    String password = passwordEncoder.encode("123456");
    return new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

(4) Write UserController.java

@Controller
@RequestMapping("user")
public class UserController {
    @RequestMapping("/getCurrentUser")
    @ResponseBody
    public Object getCurrentUser(Authentication authentication) {
        return authentication.getPrincipal();
    }
}

(5) Write the configuration class oauth2config. Java
Spring Security configuration class

@SpringBootConfiguration
@EnableWebSecurity
public class Oauth2Config extends WebSecurityConfigurerAdapter {

    //Configure the implementation class object of passwordEncode
    @Bean
    public PasswordEncoder getPE(){
        return new BCryptPasswordEncoder();
    }

    //Configure permissions

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Turn off csrf
        http.csrf() .disable()
                //Release request
                .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**", "/logout/**")
                .permitAll()
                //All requests except the above are blocked
                .anyRequest() .authenticated()
                .and()
                //Release login interface
                .formLogin() .permitAll();
    }
}

Authorization server configuration AuthorizationServerConfig.java

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //Configure client_id
                .withClient("admin")
                //Configure client secret
                .secret(passwordEncoder.encode("112233"))
                //Configure redirect_uri, used to jump after successful authorization
                .redirectUris("http://www.baidu.com")
                //Configure the requested permission range
                .scopes("all")
                //Configure grant_type indicates the authorization type
                .authorizedGrantTypes("authorization_code","password");
    }
}

Resource server configuration ResourceServerConfig.java

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .requestMatchers()
                .antMatchers("/user/**");//Configure the resource path to be protected
    }
}

test result

Obtain authorization code:
http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all

After obtaining the authorization code, use PostMan software to http://localhost:8080/oauth/token Access and configure:



Get token after sending request ---------

Configure tokens for access on: http://localhost:8080/user/getCurrentUser , get resources through tokens

If the token token is modified, an error will be reported

② . password mode

Unfinished to be continued

Keywords: Java Framework Spring Security oauth2

Added by scottb1 on Fri, 05 Nov 2021 21:05:23 +0200