亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot?Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理

 更新時間:2022年11月07日 17:05:07   作者:allway2  
安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理Spring?Security基本配置

在本教程中,我將指導(dǎo)您如何編寫代碼,以使用具有基于表單的身份驗(yàn)證的Spring安全API來保護(hù)Spring Boot應(yīng)用程序中的網(wǎng)頁。用戶詳細(xì)信息存儲在MySQL數(shù)據(jù)庫中,并使用春季JDBC連接到數(shù)據(jù)庫。我們將從本教程中的 ProductManager 項(xiàng)目開始,向現(xiàn)有的彈簧啟動項(xiàng)目添加登錄和注銷功能。

1. 創(chuàng)建用戶表和虛擬憑據(jù)

憑據(jù)應(yīng)存儲在數(shù)據(jù)庫中,因此讓我們創(chuàng)建新表,表間關(guān)系ER圖如下:

-- --------------------------------------------------------
-- 主機(jī):                           127.0.0.1
-- 服務(wù)器版本:                        8.0.22 - MySQL Community Server - GPL
-- 服務(wù)器操作系統(tǒng):                      Win64
-- HeidiSQL 版本:                  12.1.0.6537
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- 導(dǎo)出 product3 的數(shù)據(jù)庫結(jié)構(gòu)
CREATE DATABASE IF NOT EXISTS `product3` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `product3`;
-- 導(dǎo)出  表 product3.permission 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `uri` varchar(8192) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `method` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.permission 的數(shù)據(jù):~5 rows (大約)
INSERT INTO `permission` (`id`, `name`, `description`, `uri`, `method`) VALUES
	(3, 'product_create', '增加產(chǎn)品', '/new', 'GET'),
	(4, 'product_delete', '刪除產(chǎn)品', '/delete/*', 'GET'),
	(5, 'product_save', '保存產(chǎn)品', '/save', 'POST'),
	(9, 'product_read', '讀取產(chǎn)品', '/', 'GET'),
	(10, 'product_edit', '編輯產(chǎn)品', '/edit/*', 'GET');
-- 導(dǎo)出  表 product3.product 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `product` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `brand` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `madein` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `price` float NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.product 的數(shù)據(jù):~2 rows (大約)
INSERT INTO `product` (`id`, `brand`, `madein`, `name`, `price`) VALUES
	(6, '6', '6', '6', 6),
	(7, '7', '7', '7', 7);
-- 導(dǎo)出  表 product3.role 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.role 的數(shù)據(jù):~3 rows (大約)
INSERT INTO `role` (`id`, `name`, `description`) VALUES
	(1, 'ADMIN', 'Administrator role'),
	(2, 'USER_P1', 'Perfil 1'),
	(3, 'USER_P2', 'Perfil 2');
-- 導(dǎo)出  表 product3.role_permission 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `role_permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NOT NULL DEFAULT '0',
  `permission_id` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `role_id_permission_id` (`role_id`,`permission_id`),
  KEY `FK_role_permission_permission` (`permission_id`),
  CONSTRAINT `FK_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`),
  CONSTRAINT `FK_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.role_permission 的數(shù)據(jù):~10 rows (大約)
INSERT INTO `role_permission` (`id`, `role_id`, `permission_id`) VALUES
	(1, 1, 3),
	(2, 1, 4),
	(3, 1, 5),
	(4, 1, 9),
	(5, 1, 10),
	(8, 2, 5),
	(6, 2, 9),
	(7, 2, 10),
	(10, 3, 4),
	(9, 3, 9);
-- 導(dǎo)出  表 product3.urls 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `urls` (
  `name` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.urls 的數(shù)據(jù):~0 rows (大約)
-- 導(dǎo)出  表 product3.user 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name` varchar(65) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `enabled` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.user 的數(shù)據(jù):~3 rows (大約)
INSERT INTO `user` (`id`, `username`, `email`, `name`, `password`, `enabled`) VALUES
	(1, 'admin', 'admin@example.com', 'Administrator', '$2a$10$2/LSmp3YoEOT97KzgrYODen7I88ErBovM2Qehw9DL1dW9DZ7DZSAm', 1),
	(2, 'u1', 'u1@example.com', 'User P1', '$2a$10$2/LSmp3YoEOT97KzgrYODen7I88ErBovM2Qehw9DL1dW9DZ7DZSAm', 1),
	(3, 'u2', 'u2@example.com', 'User P2', '$2a$10$2/LSmp3YoEOT97KzgrYODen7I88ErBovM2Qehw9DL1dW9DZ7DZSAm', 1);
-- 導(dǎo)出  表 product3.user_role 結(jié)構(gòu)
CREATE TABLE IF NOT EXISTS `user_role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL DEFAULT '0',
  `role_id` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id_role_id` (`user_id`,`role_id`),
  KEY `FK_user_role_role` (`role_id`),
  CONSTRAINT `FK_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
  CONSTRAINT `FK_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 正在導(dǎo)出表  product3.user_role 的數(shù)據(jù):~3 rows (大約)
INSERT INTO `user_role` (`id`, `user_id`, `role_id`) VALUES
	(1, 1, 1),
	(2, 2, 2),
	(3, 3, 3);
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

2. 配置數(shù)據(jù)源屬性

接下來,在應(yīng)用程序?qū)傩晕募兄付〝?shù)據(jù)庫連接信息,如下所示:根據(jù)您的MySQL數(shù)據(jù)庫更新URL,用戶名和密碼。

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/product3?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#logging.level.root=WARN

3. 聲明彈簧安全性和MySQL JDBC驅(qū)動程序的依賴關(guān)系

要將Spring安全API用于項(xiàng)目,請?jiān)趐om.xml文件中聲明以下依賴項(xiàng):并且要將JDBC與彈簧啟動和MySQL一起使用:請注意,依賴項(xiàng)版本已由彈簧啟動初學(xué)者父項(xiàng)目定義。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>net.codejava</groupId>
    <artifactId>ProductManagerJDBCAuthenticationManuallyAuthenticateCaptchaAccess</artifactId>
    <version>2.0</version>
    <name>ProductManagerJDBCAuthenticationManuallyAuthenticateCaptchaAccess</name>
    <description>ProductManagerJDBCAuthentication</description>
    <packaging>jar</packaging>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4. 配置 JDBC 身份驗(yàn)證詳細(xì)信息

要將 Spring 安全性與基于表單的身份驗(yàn)證和 JDBC 結(jié)合使用,請按如下方式創(chuàng)建WebSecurityConfig類:

package com.example;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder.jdbcAuthentication()
                .dataSource(dataSource)
                .passwordEncoder(new BCryptPasswordEncoder())
                .usersByUsernameQuery("select username, password, enabled from user where username=?")
                .authoritiesByUsernameQuery("SELECT user.username,permission.name FROM user,role,user_role,permission,role_permission WHERE user.id=user_role.user_id AND role.id=user_role.role_id AND role.id=role_permission.role_id AND permission.id=role_permission.permission_id  AND user.username=?");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/common/**").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/logout").permitAll()
                .antMatchers("/verify").permitAll()
                .anyRequest()
                .access("@rbacService.hasPermission(request , authentication)")
                .and()
                .formLogin().loginPage("/login")
                .permitAll()
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/403");
    }
}

此安全配置類必須使用@EnableWebSecurity注釋進(jìn)行批注,并且是Web 安全配置器適配器的子類。數(shù)據(jù)源對象的實(shí)例將由Spring框架創(chuàng)建并注入:

    @Autowired
    private DataSource dataSource;

它將從應(yīng)用程序?qū)傩晕募凶x取數(shù)據(jù)庫連接信息。要使用JDBC配置身份驗(yàn)證,請編寫以下方法:

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder.jdbcAuthentication()
                .dataSource(dataSource)
                .passwordEncoder(new BCryptPasswordEncoder())
                .usersByUsernameQuery("select username, password, enabled from users where username=?")
                .authoritiesByUsernameQuery("SELECT users.username,permissions.name FROM users,roles,users_roles,permissions,roles_permissions WHERE users.username=users_roles.username AND roles.name=users_roles.role_name AND roles.name=roles_permissions.role_name AND permissions.name=roles_permissions.permission AND users.username=?");
    }

如您所見,我們需要指定密碼編碼器(建議使用BCrypt),數(shù)據(jù)源和兩個SQL語句:第一個根據(jù)用戶名選擇用戶,第二個選擇用戶的角色。請注意,Spring安全性要求列名必須是用戶名,密碼,啟用和角色。為了配置基于表單的身份驗(yàn)證,我們重寫了 configure(HttpSecurity)方法,如下所示:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/edit/*", "/delete/*").hasAnyAuthority("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll()
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/403");
    }

在這里,我們指定所有請求都必須進(jìn)行身份驗(yàn)證,這意味著用戶必須登錄才能使用該應(yīng)用程序。使用Spring安全性提供的默認(rèn)登錄表單。要顯示已登錄用戶的用戶名,請?jiān)赥hymeleaf模板文件中編寫以下代碼:

<div sec:authorize="isAuthenticated()">
                Welcome <b><span sec:authentication="name">Username</span></b>
                &nbsp;
                <i><span sec:authentication="principal.authorities">Roles</span></i>
            </div>

并添加注銷按鈕:

            <form th:action="@{/logout}" method="post">
                <input type="submit" value="Logout" />
            </form>

如您所見,Spring Security將處理應(yīng)用程序的登錄和注銷。我們不必編寫重復(fù)的代碼,只需指定一些配置即可。

5. 自定義登錄驗(yàn)證過程

package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
@Controller
public class LoginController {
    /**
     * 注入身份認(rèn)證管理器
     */
    @Autowired
    private AuthenticationManager authenticationManager;
    @GetMapping("/login")
    public String login() {
        return "login";
    }
    @PostMapping(value = "/verify")
    public String login(@RequestParam("username") String username,
            @RequestParam("password") String password,
            @RequestParam("verifyCode") String verifyCode,
            HttpSession session) {
        System.out.println("username is:" + username);
        System.out.println("password is:" + password);
        System.out.println("verifyCode is:" + verifyCode);
        if (StringUtils.isEmpty(verifyCode)) {
            session.setAttribute("errorMsg", "The verification code cannot be empty");
            return "login";
        }
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            session.setAttribute("errorMsg", "User name or password cannot be empty");
            return "login";
        }
        String kaptchaCode = session.getAttribute("verifyCode") + "";
        System.out.println("kaptchaCode is:" + kaptchaCode);
        if (StringUtils.isEmpty(kaptchaCode) || !verifyCode.equals(kaptchaCode)) {
            session.setAttribute("errorMsg", "Verification code error");
            return "login";
        }
//        User user = userService.login(userName, password);
        System.out.println(username + "==" + password + "==" + verifyCode);
        // 創(chuàng)建用戶名與密碼認(rèn)證對象
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        try {
            // 調(diào)用認(rèn)證方法,返回認(rèn)證對象
            Authentication authenticate = authenticationManager.authenticate(token);
            // 判斷是否認(rèn)證成功
            if (authenticate.isAuthenticated()) {
                // 設(shè)置用戶認(rèn)證成功,往Session中添加認(rèn)證通過信息
                SecurityContextHolder.getContext().setAuthentication(authenticate);
                SecurityContext sc = SecurityContextHolder.getContext();
                sc.setAuthentication(authenticate);
                session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
                // 重定向到登錄成功頁面
                return "redirect:/";
            } else {
                session.setAttribute("errorMsg", "Login failed");
                return "login";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "login";
    }
}

kaptcha驗(yàn)證碼

package net.codejava;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@Controller
public class KaptchaController {
    @Autowired
    private DefaultKaptcha captchaProducer;
    @GetMapping("/common/kaptcha")
    public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        byte[] captchaOutputStream = null;
        ByteArrayOutputStream imgOutputStream = new ByteArrayOutputStream();
        try {
            //Produce the verification code string and save it in the session
            String verifyCode = captchaProducer.createText();
            httpServletRequest.getSession().setAttribute("verifyCode", verifyCode);
            BufferedImage challenge = captchaProducer.createImage(verifyCode);
            ImageIO.write(challenge, "jpg", imgOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        captchaOutputStream = imgOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaOutputStream);
        responseOutputStream.flush();
        responseOutputStream.close();
    }
}
package net.codejava;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.put("kaptcha.border", "no");
        properties.put("kaptcha.textproducer.font.color", "black");
        properties.put("kaptcha.image.width", "150");
        properties.put("kaptcha.image.height", "40");
        properties.put("kaptcha.textproducer.font.size", "30");
        properties.put("kaptcha.session.key", "verifyCode");
        properties.put("kaptcha.textproducer.char.space", "5");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

6. 登錄頁面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Bootstrap 5 Sign In Form with Image Example</title>
        <link  rel="external nofollow"  rel="stylesheet"
              integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
                integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
        <link rel="stylesheet"  rel="external nofollow" >
    </head>
    <body>
        <form th:action="@{/verify}" method="post">
            <div class="container-fluid vh-100" style="margin-top:50px">
                <div class="" style="margin-top:50px">
                    <div class="rounded d-flex justify-content-center">
                        <div class=" col-md-4 col-sm-12 shadow-lg p-5 bg-light">
                            <div class="text-center">
                                <h3 class="text-primary">請登錄</h3>
                            </div>
                            <div class="p-4">
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-person-fill text-white"></i></span>
                                    <input id="username" type="text" name="username" required class="form-control" placeholder="用戶名">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-key-fill text-white"></i></span>
                                    <input  id="password" type="password" name="password" required class="form-control" placeholder="密碼">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-lock-fill text-white"></i></span>
                                    <input type="text" name="verifyCode"  class="form-control" placeholder="輸入下圖中的校驗(yàn)碼">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-image-fill text-white"></i></span>
                                    <img alt="Click the picture to refresh!" class="pointer" th:src="@{/common/kaptcha}"
                                         onclick="this.src = '/common/kaptcha?d=' + new Date() * 1">
                                </div>
                                <div class="col-12">
                                    <button type="submit" class="btn btn-primary px-4 float-end mt-4">登錄</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
    </body>
</html>

7. 測試登錄和注銷

啟動Spring Boot應(yīng)用程序并訪問 http://localhost:8080 在Web瀏覽器中,您將看到自定義的登錄頁面出現(xiàn):

現(xiàn)在輸入正確的用戶名admin和密碼admin,您將看到主頁如下:

并注意歡迎消息后跟用戶名。用戶現(xiàn)在已通過身份驗(yàn)證以使用該應(yīng)用程序。單擊“注銷”按鈕,您將看到自定義的登錄頁面出現(xiàn),這意味著我們已成功實(shí)現(xiàn)登錄并注銷到我們的Spring Boot應(yīng)用程序。

自定義從數(shù)據(jù)庫中獲取動態(tài)權(quán)限驗(yàn)證

package com.example;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Data
@Entity
public class Permission {
    @Id
    private Long id;
    private String name;
    private String description;
    private String uri;
    private String method;
}
package com.example;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PermissionRepository extends JpaRepository<Permission, Long> {
    public Permission findByName(String name);
}
package com.example;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
/**
 * RBAC模型實(shí)現(xiàn)Security,即通過角色對用戶進(jìn)行分組,在對每個角色進(jìn)行權(quán)限授權(quán)就, 進(jìn)而簡化用戶權(quán)限分配以及管理。 需要的表: user:
 * 用戶信息表 保存有的用戶id,用戶名、密碼、賬號、狀態(tài)、salt加鹽 role:角色表 user_role:用戶角色關(guān)聯(lián)表 permission: 權(quán)限表
 * 保存的有權(quán)限相關(guān)信息 role_permission: 角色與權(quán)限管理表
 */
public interface RbacService {
    //用戶判斷當(dāng)前請求是否有操作權(quán)限
    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
package com.example;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
@Component("rbacService")
public class RbacServiceImpl implements RbacService {
    @Autowired
    private PermissionRepository permissionRepository;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        //獲取用戶認(rèn)證信息
        System.out.println(authentication.getAuthorities());
        Object principal = authentication.getPrincipal();
        System.out.println(principal.getClass());
        //判斷數(shù)據(jù)是否為空 以及類型是否正確
        if (null != principal && principal instanceof User) {
            String username = ((User) principal).getUsername();
            System.out.println(username);
        }
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        String method = request.getMethod();
        System.out.println(method);
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        boolean hasPermission = false;
        for (GrantedAuthority authority : authorities) {
            String authorityname = authority.getAuthority();
            System.out.println(authority.getAuthority());
            Permission permission = permissionRepository.findByName(authorityname);
            System.out.println(permissionRepository.findByName(authorityname));
            if (null != permission && permission.getMethod().equals(request.getMethod()) && antPathMatcher.match(permission.getUri(), request.getRequestURI())) {
                hasPermission = true;
                break;
            }
        }
        System.out.println(hasPermission);
        return hasPermission;
    }
}

thymeleaf視圖文件中,根據(jù)權(quán)限顯示連接菜單

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
    <head>
        <meta charset="ISO-8859-1">
        <title>Product Manager</title>
    </head>
    <body>
        <div align="center">
            <div sec:authorize="isAuthenticated()">
                Welcome <b><span sec:authentication="name">Username</span></b>
                &nbsp;
                <i><span sec:authentication="principal.authorities">Roles</span></i>
            </div>
            <form th:action="@{/logout}" method="post">
                <input type="submit" value="Logout" />
            </form>
            <h1>Product Manager</h1>
            <div sec:authorize="hasAnyAuthority('product_create')">
                <a href="/new" rel="external nofollow" >Create New Product</a>
            </div>
            <br/><br/>
            <table border="1" cellpadding="10">
                <thead>
                    <tr>
                        <th>Product ID</th>
                        <th>Name</th>
                        <th>Brand</th>
                        <th>Made In</th>
                        <th>Price</th>
                        <th sec:authorize="hasAnyAuthority('product_edit', 'product_delete')">Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <tr th:each="product : ${listProducts}">
                        <td th:text="${product.id}">Product ID</td>
                        <td th:text="${product.name}">Name</td>
                        <td th:text="${product.brand}">Brand</td>
                        <td th:text="${product.madein}">Made in</td>
                        <td th:text="${product.price}">Price</td>
                        <td>
                            <a sec:authorize="hasAuthority('product_edit')" th:href="@{'/edit/' + ${product.id}}" rel="external nofollow" >Edit</a>
                            &nbsp;&nbsp;&nbsp;&nbsp;
                            <a sec:authorize="hasAuthority('product_delete')" th:href="@{'/delete/' + ${product.id}}" rel="external nofollow" >Delete</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </body>
</html>

結(jié)論

到目前為止,您已經(jīng)學(xué)會了使用基于表單的身份驗(yàn)證和數(shù)據(jù)庫內(nèi)憑據(jù)來保護(hù)Spring Boot應(yīng)用程序。您會看到 Spring 安全性使實(shí)現(xiàn)登錄和注銷功能變得非常容易,并且非常方便。為方便起見,您可以下載下面的示例項(xiàng)目。

下載源碼:傳送門

到此這篇關(guān)于SpringBoot Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理的文章就介紹到這了,更多相關(guān)SpringBoot Security內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Springboot之Logback的使用學(xué)習(xí)

    詳解Springboot之Logback的使用學(xué)習(xí)

    Logback是SpringBoot內(nèi)置的日志處理框架,你會發(fā)現(xiàn)spring-boot-starter其中包含了spring-boot-starter-logging,該依賴內(nèi)容就是Spring Boot默認(rèn)的日志框架logback,本文詳細(xì)介紹了該框架 ,需要的朋友可以參考下
    2021-05-05
  • 一文告訴你為什么要重寫hashCode()方法和equals()方法

    一文告訴你為什么要重寫hashCode()方法和equals()方法

    本篇文章帶大家了解一下為什么重寫hashCode()方法和equals()方法,文中有非常詳細(xì)的說明以及代碼示例,對正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • IDEA中的maven沒有dependencies解決方案

    IDEA中的maven沒有dependencies解決方案

    這篇文章主要介紹了IDEA中的maven沒有dependencies解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • SpringDataJpa的@Query注解報錯的解決

    SpringDataJpa的@Query注解報錯的解決

    這篇文章主要介紹了SpringDataJpa的@Query注解報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java list如何根據(jù)id獲取子節(jié)點(diǎn)

    Java list如何根據(jù)id獲取子節(jié)點(diǎn)

    這篇文章主要介紹了Java list如何根據(jù)id獲取子節(jié)點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解

    Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解

    這篇文章主要介紹了Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-07-07
  • Java異步處理機(jī)制實(shí)例詳解

    Java異步處理機(jī)制實(shí)例詳解

    本文涉及Java編程中異步處理機(jī)制的簡單介紹和一個相關(guān)實(shí)例,相信通過這篇文章,大家能對異步處理有更多的了解。
    2017-09-09
  • Vue中computed計算屬性和data數(shù)據(jù)獲取方式

    Vue中computed計算屬性和data數(shù)據(jù)獲取方式

    這篇文章主要介紹了Vue中computed計算屬性和data數(shù)據(jù)獲取方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 一文搞懂Spring AOP的五大通知類型

    一文搞懂Spring AOP的五大通知類型

    本文將詳細(xì)為大家介紹Spring AOP的五種通知類型(前置通知、后置通知、返回通知、異常通知、環(huán)繞通知),感興趣的朋友可以了解一下
    2022-06-06
  • SpringBoot配置類編寫過程圖解

    SpringBoot配置類編寫過程圖解

    這篇文章主要介紹了SpringBoot配置類編寫過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11

最新評論