【WEEK12】 【DAY5】Building Spring Security Environment Part Two【English Version】

2024.5.17 Friday
Following the article【WEEK12】 【DAY4】Building Spring Security Environment Part One【English Version】

14.5. Logging Out and Access Control

14.5.1. Adding Functionality to Redirect to Main Page upon Logout

Modifying SecurityConfig.java

package com.P34.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

// Implement interceptor functionality using AOP
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //alt+insert->override methods
    //Method chaining
    //Authorization
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Everyone can access the home page, functional pages are only open to authorized users
        // Request authorization rules
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        // Redirect to login page if not authorized, can check source code of formLogin for more details
        http.formLogin();

        // Enable logout functionality, redirect to home page after logout
        http.logout().logoutSuccessUrl("/");
    }
//    public WebSecurityCustomizer webSecurityCustomizer() - New method

    //Authentication
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Syntax example visible by opening WebSecurityConfigurerAdapter at line 194:
        /**
         * protected void configure(AuthenticationManagerBuilder auth) {
         *     auth
         *     // enable in memory based authentication with a user named
         *     // "user" and "admin"
         *     .inMemoryAuthentication().withUser("user").password("password").roles("USER").and()
         *           .withUser("admin").password("password").roles("USER", "ADMIN");
         * }
         */
        // These data should theoretically be retrieved from the database
        // Running it shows "There was an unexpected error (type=Internal Server Error, status=500)".
        // The password encoding is missing (There is no PasswordEncoder mapped for the id “null”), so we need to encrypt the "password" in the code
        // Many encryption methods have been added in Spring Security 5.0+: click BCryptPasswordEncoder->PasswordEncoder->click the green "I" on the left sidebar (see image below)
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("Zzz").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip3");
    }
}

Restart and login with any user to verify logout functionality.
Logout functionality
Logout functionality
Upon logout, redirected to:
Redirect upon logout

Regardless of which level option is clicked, the user is prompted to log in (redirected to the login page).

14.5.2. Modifying the Display Effect After Login

14.5.2.1. Adding thymeleaf-security Dependency in pom.xml

https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5/3.0.4.RELEASE

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

Version selection:

  • Integration Package 4 (springsecurity4) — for Spring Boot version 2.0.9
  • Integration Package 5 (springsecurity5) — for Spring Boot version after 2.0.9

14.5.2.2. Modifying index.html

Importing namespaces and adding conditional statements to determine the display of the top navigation bar based on login status. Adding functionality to display the username and its authorities, and displaying corresponding accessible pages based on permissions.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<!-- The URL on the previous line is just for reference -->
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>Home</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
    <link th:href="@{/Zzz/css/qinstyle.css}" rel="stylesheet">
</head>
<body>

<!-- Main Container -->
<div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item"  th:href="@{/index}">Home</a>

            <!-- Login/logout -->
            <div class="right menu">

                <!-- If not logged in -->
                <div sec:authorize="!isAuthenticated()"><!-- Check if logged in -->
                    <a class="item" th:href="@{/toLogin}">
                        <i class="address card icon"></i> Login
                    </a>
                </div>

                <!-- If logged in -->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        <i class="address card icon"></i>
                        Username: <span sec:authentication="principal.username"></span>
                        Authorities: <span sec:authentication="principal.authorities"></span>
                        <!-- The syntax for 'principal' here is different from thymeleaf-extras-springsecurity4 version -->
                    </a>
                </div>

                <div sec:authorize="isAuthenticated()">
                    <a class="item" th:href="@{/logout}">
                        <i class="sign-out  icon"></i> Logout
                    </a>
                </div>
            </div>
        </div>
    </div>

    <div class="ui segment" style="text-align: center">
        <h3>Spring Security Study</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">

            <!-- Menu implemented dynamically based on user roles -->
            <div class="column"  sec:authorize="hasRole('vip1')"><!-- If has vip1 permission, display level1 content on the homepage, otherwise, don't display -->
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column"  sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column"  sec:authorize="hasRole('vip3')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
    
</div>

<!-- Importing JS libraries -->
<script th:src="@{/Zzz/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/Zzz/js/semantic.min.js}"></script>

</body>
</html>

14.5.2.3. Restart

Restart
Restart
Re-login after logout:
Re-login
Re-login
Re-login
Re-login

14.6. “Remember Me” Functionality

14.6.1. Modifying SecurityConfig.java

package com.P34.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

// Using AOP concept to implement interceptor functionality
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // alt+insert->override methods
    // Chained programming
    // Authorization
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Everyone can access the homepage, functional pages are only open to users with permissions
        // Request authorization rules
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        // If no permission, default to the login page, can open formLogin to view source code
        http.formLogin();

        // Enable logout function, redirect to the homepage after logout
        http.logout().logoutSuccessUrl("/");

        // Enable "Remember Me" function (cookie implementation), default duration: 2 weeks
        http.rememberMe();
    }

    // Authentication
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Clicking on line 194 of WebSecurityConfigurerAdapter reveals syntax examples:
        /**
         * protected void configure(AuthenticationManagerBuilder auth) {
         *     auth
         *     // enable in memory based authentication with a user named
         *     // &quot;user&quot; and &quot;admin&quot;
         *     .inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
         *           .withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
         * }
         */
        // These data should theoretically be read from the database
        // Running it will display an error (type=Internal Server Error, status=500).
        // It's because the password encoding is not encrypted (There is no PasswordEncoder mapped for the id “null”), so the password in the code needs to be encrypted
        // In Spring Security 5.0+, many encryption methods have been added: you can click BCryptPasswordEncoder->PasswordEncoder->click on the green "I" in the left sidebar (as shown below)
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("Zzz").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip3");
    }
}

14.6.2. Restart

Choose any user to log in, and check “Remember me on this computer.”
Remember me
Now, open a new web page, access http://localhost:8080/, and the login status is already present.
Login status
Inspect using F12:
Inspect
After clearing cookies and refreshing the page, return to the login page.
Return to login

14.7. Customizing the Login Page

14.7.1. Modifying SecurityConfig.java

package com.P34.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

// Using AOP concept to implement interceptor functionality
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // alt+insert->override methods
    // Chained programming
    // Authorization
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Everyone can access the homepage, functional pages are only open to users with permissions
        // Request authorization rules
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        // If no permission, default to the login page, can open formLogin to view source code (line 2202: public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception)
        http.formLogin().loginPage("/toLogin").usernameParameter("username1").passwordParameter("password1").loginProcessingUrl("/login");
        //.usernameParameter("username1").passwordParameter("password1") These two settings are because the names used in the login.html file are not default, so they need to be reconfigured here to be able to recognize and login normally. Otherwise, the URL will become http://localhost:8080/toLogin?error and cannot login normally.

        // Enable logout function, redirect to the homepage after logout
        http.logout().logoutSuccessUrl("/");

        // If getting a 404 error on logout, it's because it defaults to preventing CSRF cross-site request forgery, which can cause security issues. We can change the request to post form submission, or disable CSRF functionality in Spring Security by adding the next line of code to solve the problem
        http.csrf().disable();  // Disable CSRF protection: Cross-Site Request Forgery, by default, only logout requests can be submitted via the post method

        // Enable "Remember Me" function (cookie implementation), default duration: 2 weeks
        http.rememberMe().rememberMeParameter("remember");
    }

    // Authentication
    ......
}

14.7.2. Modifying index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<!-- The above line of URL is just for reference -->
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>Homepage</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
    <link th:href="@{/Zzz/css/qinstyle.css}" rel="stylesheet">
</head>
<body>

<!-- Main container -->
<div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item"  th:href="@{/index}">Homepage</a>

            <!-- Login and logout -->
            <div class="right menu">

                <!-- If not logged in -->
                <div sec:authorize="!isAuthenticated()"><!-- Check if logged in -->
                    <a class="item" th:href="@{/toLogin}">
                        <i class="address card icon"></i> Login
                    </a>
                </div>

                <!-- If logged in -->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        <i class="address card icon"></i>
                        Username: <span sec:authentication="principal.username"></span>
                        Authorization: <span sec:authentication="principal.authorities"></span>
                        <!-- The syntax of principal here is different from thymeleaf-extras-springsecurity4 version -->
                    </a>
                </div>

                <div sec:authorize="isAuthenticated()">
                    <a class="item" th:href="@{/logout}">
                        <i class="sign-out  icon"></i> Logout
                    </a>
                </div>
            </div>
        </div>
    </div>

    <div class="ui segment" style="text-align: center">
        <h3>Spring Security Study</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">

            <!-- Menu dynamically implemented based on user roles -->
            <div class="column"  sec:authorize="hasRole('vip1')"><!-- Show level1 content on the homepage if there is vip1 permission, otherwise do not display -->
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column"  sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column"  sec:authorize="hasRole('vip3')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
    
</div>


<script th:src="@{/Zzz/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/Zzz/js/semantic.min.js}"></script>

</body>
</html>

14.7.3. Modifying login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>Login</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
</head>
<body>

<!-- Main container -->
<div class="ui container">

    <div class="ui segment">

        <div style="text-align: center">
            <h1 class="header">Login</h1>
        </div>

        <div class="ui placeholder segment">
            <div class="ui column very relaxed stackable grid">
                <div class="column">
                    <div class="ui form">
                        <form th:action="@{/login}" method="post">
                            <div class="field">
                                <label>Username</label>
                                <div class="ui left icon input">
                                    <input type="text" placeholder="Username" name="username1">
                                    <!-- The reason for naming it username and password is because the annotation in HttpSecurity.java is default is username and default is password -->
                                    <!-- If you want to use custom names (changed here to username1 and password1), you need to modify SecurityConfig -->
                                    <i class="user icon"></i>
                                </div>
                            </div>
                            <div class="field">
                                <label>Password</label>
                                <div class="ui left icon input">
                                    <input type="password" name="password1">
                                    <i class="lock icon"></i>
                                </div>
                            </div>
                            <div class="field"><!-- Centered -->
                                <input type="checkbox" name="remember"> Remember me
                            </div>
                            <input type="submit" class="ui blue submit button"/>
                        </form>
                    </div>
                </div>
            </div>
        </div>

        <div style="text-align: center">
            <div class="ui label">
                </i>Register
            </div>
            <br><br>
            <small>https://blog.csdn.net/2401_83329143</small>
        </div>
        <div class="ui segment" style="text-align: center">
            <h3>Spring Security Study</h3>
        </div>
    </div>


</div>

<script th:src="@{/Zzz/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/Zzz/js/semantic.min.js}"></script>

</body>
</html>

14.7.4. Restart

At this point, you cannot access the login page by visiting http://localhost:8080/login
Image

You can only access the login page by clicking “Login” in the top right corner of the homepage
Image
Image
Image
After clicking “Logout” in the top right corner, you will return to the login page.

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值