Spring Security 認證授權安全框架

Spring Security概述

1.什么是Spring Security?

Spring Security是一個Java框架,用于保護應用程序的安全性。它提供了一套全面的安全解決方案,包括身份驗證、授權、防止攻擊等功能。Spring Security基于過濾器鏈的概念,可以輕松地集成到任何基于Spring的應用程序中。它支持多種身份驗證選項和授權策略,開發人員可以根據需要選擇適合的方式。此外,Spring Security還提供了一些附加功能,如集成第三方身份驗證提供商和單點登錄,以及會話管理和密碼編碼等。總之,Spring Security是一個強大且易于使用的框架,可以幫助開發人員提高應用程序的安全性和可靠性。

什么是授權

這個根據用戶的權限來控制用戶使用資源的過程就是授權。

為什么要授權

認證是為了保護用戶身份的合法性,授權則是為了更細的粒度的對隱私數據經行劃分,授權是在認證通過后發生的,控制不同的用戶能夠訪問不同的資源;

授權:授權是用戶認證通過根據用戶的權限來控制用戶訪問資源的過程,擁有資源的訪問權限則正常訪問,沒有權限則拒絕訪問;

Spring Security入門體驗

一.快速搭建Spring Security安全框架項目;

1.1--創建一個SpringBoot項目;

1.2--修改SpringBoot的版本號,并修改jdk版本號;

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>SpringSecurity001</artifactId><version>0.0.1-SNAPSHOT</version><name>SpringSecurity001</name><description>SpringSecurity001</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.6.13</spring-boot.version><!--SpringBoot版本號   這里使用的JDK版本為8--></properties><dependencies><!--引入Spring Security安全框架依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--添加lombok依賴  實體類的get set方法生成--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--測試依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.aaa.SpringSecurity001Application</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

1.3--引入SPringleSecurity依賴;

<!--引入Spring Security安全框架依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

1.4--創建一個HelloController,Controller層定義一個資源;

1.5--啟動項目并訪問資源路徑;

訪問路徑:http://localhost:8080/hello

注:我們發現使用了security后在訪問我們自己的接口,security會攔截并跳轉到認證頁面,認證后才可以訪問。默認認證的賬號uer密碼在控制臺;

二.創建Spring Security自定義賬戶和密碼;

我們剛才使用的是security自帶的賬戶和密碼,我們自己也可以自定義賬戶和密碼;

2.1--在application.properties配置文件中定義一個賬戶和密碼;

自定義賬戶和密碼文件如下:

#創建Spring Security自定義賬戶和密碼#賬戶
spring.security.user.name=admin
#密碼
spring.security.user.password=123456
server.port=8080

注:配置完自定義文件和密碼,重啟運行,這是控制臺中沒有密碼(控制臺不會生成密碼);

注:這種方式只能定義一個賬戶和密碼,無法定義多個賬戶和密碼;

三.自定義多個賬戶和密碼

在配置文件中,只能定義一個賬戶和密碼。我們可以定義一個配置類,完成多個賬號和密碼的定義;

1.創建Config層 MysecurityConfig;

配置類內容

package com.aaa.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于內存創建的賬戶和密碼  未來可以用來連接數據庫//賬戶.withUser("zhangsan")//密碼.password("123456")//用戶具備的角色.roles("admin").and() //連接符.withUser("lisi").password("012345").roles("test").and().withUser("wangwu").password("567890").roles("group");}
}

注:如果使用了配置類,那么之前在配置文件中定義的賬戶和密碼不在生效;

控制臺錯誤提示:需要給它指定的加密器;

解決方式:添加加密器

//可以把該方法返回的對象交于Spring容器管理@Beanpublic PasswordEncoder passwordEncoder(){return  new BCryptPasswordEncoder();}
package com.aaa.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {//可以把該方法返回的對象交于Spring容器管理@Beanpublic PasswordEncoder passwordEncoder(){return  new BCryptPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于內存創建的賬戶和密碼  未來可以用來連接數據庫//賬戶.withUser("zhangsan")//密碼.password(passwordEncoder().encode("123456"))//用戶具備的角色.roles("admin").and() //連接符.withUser("lisi").password(passwordEncoder().encode("012345")).roles("test").and().withUser("wangwu").password(passwordEncoder().encode("567890")).roles("group");}
}

注:把明文密碼通過加密器經行加密;

再次訪問資源

四.密碼加密器

密碼加密器,可以把把明文轉換為密文 encode

同時也可以匹配密碼 matches;

package com.aaa.Test;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;public class test {public static void main(String[] args) {//創建加密器對象PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//使用加密器String encode = passwordEncoder.encode("123456");System.out.println(encode);//匹配密碼  第一個參數是客戶輸得密碼   第二個參數是加密器密文//把客戶輸得密碼和加密過的密碼經行比對是否一致,一致返回true,不一致返回flase;boolean matches = passwordEncoder.matches("123456", encode);//結果為trueSystem.out.println(matches);   }
}

?

問題:對稱加密和非對稱加密

答:對稱加密:加密和解密使用的密鑰是同一個; --這種加密的方式可以破解

??????非對稱加密: 加密和解密使用的密鑰不是同一個;--這種加密的方式無法破解

五.獲取當前用戶的信息

獲取登錄成功者的信息

package com.aaa.Controller;import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@GetMapping("/hello")public String hello(){return "hello資源·······";}@GetMapping("/info")public Authentication info(){//把當前信息保存到SecurityContext類中;SecurityContext securityContext = SecurityContextHolder.getContext();//把當前用戶封裝到Authentication類中  賬戶  密碼 權限 角色等信息Authentication authentication = securityContext.getAuthentication();return authentication;}
}

Security框架會把當前用戶信息封裝到Authentication中,并把該類對象存放到SecurityContext中;

訪問資源路徑http://localhost:8080/info

六.修改它的登錄頁面

默認security提供了一個登錄頁面,如果不想使用它提供的頁面,我們可以指定我們自己的登錄頁面;

1.自己在static靜態資源文件下創建一個登錄頁面;

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登錄頁</title><style>body {background-color: #222;font-family: Arial, sans-serif;color: #fff;display: flex;align-items: center;justify-content: center;height: 100vh;}.login-container {background-color: #333;padding: 40px;border-radius: 10px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);}h1 {text-align: center;}.form-group {margin-bottom: 20px;}.form-group label {display: block;margin-bottom: 5px;}.form-group input[type="text"],.form-group input[type="password"] {width: 100%;padding: 10px;border-radius: 5px;border: none;background-color: #555;color: #fff;}.form-group input[type="submit"] {width: 100%;padding: 10px;border-radius: 5px;border: none;background-color: #007bff;color: #fff;cursor: pointer;transition: background-color 0.3s ease;}.form-group input[type="submit"]:hover {background-color: #0056b3;}</style>
</head>
<body>
<div class="login-container"><h1>Welcome to Cool Login Page</h1><form action="login" method="post"><div class="form-group"><label for="username">Username:</label><!--賬戶--><input type="text" id="username" name="username" required></div><div class="form-group"><label for="password">Password:</label><!--密碼--><input type="password" id="password" name="password" required></div><div class="form-group"><input type="submit" value="Login"></div></form>
</div>
</body>
</html>

2.修改security配置類;在config層 HelloConfig文件中重寫configure方法;

 @Overrideprotected void configure(HttpSecurity http) throws Exception {//設置表單登錄信息http.formLogin()//設置自己的登錄頁面.loginPage("/login.html")//設置登錄表單的提交路徑 要和login.html中的action一致.loginProcessingUrl("/login")//這個頁面允許放行.permitAll();//禁止跨域偽造請求驗證http.csrf().disable();//其他請求路徑都要認證http.authorizeRequests().anyRequest().authenticated();}

訪問路徑訪問info或者hello都可以進入到自己的登錄頁面;

七.設置跳轉登錄成功頁面

默認登錄成功 / 或原來的訪問路徑

創建SuccessController

package com.aaa.Controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;@Controller
public class SuccessController {@PostMapping("/success")public String success(){return "redirect:/success.html";}
}

創建一個success.html登錄成功頁面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>登錄成功</h1>
</body>
</html>

輸入賬號和密碼

登錄成功

授權的實現

授權:用戶具有的權限和資源綁定的過程就是授權;

第一步:修改config? MySecurityConfig配置文件

?第二步:創建自定義資源訪問? controller層??AuthController資源文件

package com.aaa.Controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;//資源
@RestController
public class AuthController {@GetMapping("select")public String select(){System.out.println("查詢資源");return "查詢資源";}@GetMapping("update")public String update(){System.out.println("修改資源");return "修改資源";}@GetMapping("delete")public String delete(){System.out.println("刪除資源");return "刪除資源";}@GetMapping("xxx")public String xxx(){System.out.println("共享資源");return "共享資源";}
}

第三步:?權限和資源進行綁定? ?config? MySecurityConfig配置文件

package com.aaa.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {//可以把該方法返回的對象交于Spring容器管理@Beanpublic PasswordEncoder passwordEncoder(){return  new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {//設置表單登錄信息http.formLogin()//設置自己的登錄頁面.loginPage("/login.html")//設置登錄表單的提交路徑 要和login.html中的action一致.loginProcessingUrl("/login")//表示登錄成功跳轉路徑 提交方式必須為post請求.successForwardUrl("/success")//這個頁面允許放行.permitAll();//設定資源和權限進行綁定http.authorizeRequests().antMatchers("/select").hasAnyAuthority("user:select").antMatchers("/update").hasAnyAuthority("user:update").antMatchers("/delect").hasAnyAuthority("user:delect").antMatchers("/xxx").hasAnyAuthority("user:xxx");//異常處理頁面http.exceptionHandling().accessDeniedPage("/403.html");//禁止跨域偽造請求驗證http.csrf().disable();//其他請求路徑都要認證http.authorizeRequests().anyRequest().authenticated();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() //基于內存創建的賬戶和密碼  未來可以用來連接數據庫//賬戶.withUser("zhangsan")//密碼.password(passwordEncoder().encode("123456"))//用戶具備的角色.roles("admin")//設定用戶權限.authorities("user:select","user:update","user:delete").and() //連接符.withUser("lisi").password(passwordEncoder().encode("012345")).roles("test").authorities("user:xxx").and().withUser("wangwu").password(passwordEncoder().encode("567890")).roles("group");}
}

第四步:訪問資源

上面再訪問沒有的權限資源時,出現上面的錯誤界面,這種界面對客戶友好。跳轉到一個權限不足的界面; ?

使用security注解完成授權

(思考: 上面權限和資源得到綁定 需要手動一一綁定。真正再開發中我們具有的權限和資源是非常多的。如果手動一一綁定是很麻煩的。)

第一步:開啟security權限注解驅動

第二步:再相應的資源上使用注解??

controller層??AuthController資源文件

package com.aaa.Controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;//資源
@RestController
public class AuthController {@GetMapping("select")@PreAuthorize(value = "hasAuthority('user:select')")public String select(){System.out.println("查詢資源");return "查詢資源";}@GetMapping("update")@PreAuthorize(value = "hasAuthority('user:upate')")public String update(){System.out.println("修改資源");return "修改資源";}@GetMapping("delete")@PreAuthorize(value = "hasAuthority('user:delete')")public String delete(){System.out.println("刪除資源");return "刪除資源";}@GetMapping("xxx")@PreAuthorize(value = "hasAuthority('user:xxx')")public String xxx(){System.out.println("共享資源");return "共享資源";}
}

第三步:修改security配置

第四步:訪問資源,流程是一樣的? 略

了解security認證的流程(源碼)

核心過濾器:UsernamePasswordAuthenticationFilter

使用Spring Security連接數據庫(自定義認證--需要連接數據庫)

第一步:創建securtiy數據庫

DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission`  (`perid` int(0) NOT NULL AUTO_INCREMENT,`pername` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,`percode` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (`perid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES (1, '用戶查詢', 'user:query');
INSERT INTO `sys_permission` VALUES (2, '用戶添加', 'user:add');
INSERT INTO `sys_permission` VALUES (3, '用戶修改', 'user:update');
INSERT INTO `sys_permission` VALUES (4, '用戶刪除', 'user:delete');
INSERT INTO `sys_permission` VALUES (5, '用戶導出', 'user:export');-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (`roleid` int(0) NOT NULL AUTO_INCREMENT,`rolename` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (`roleid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '管理員');
INSERT INTO `sys_role` VALUES (2, '測試人員');
INSERT INTO `sys_role` VALUES (3, '普通用戶');-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission`  (`perid` int(0) NULL DEFAULT NULL,`roleid` int(0) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES (2, 1);
INSERT INTO `sys_role_permission` VALUES (1, 1);
INSERT INTO `sys_role_permission` VALUES (3, 1);
INSERT INTO `sys_role_permission` VALUES (4, 1);
INSERT INTO `sys_role_permission` VALUES (2, 2);
INSERT INTO `sys_role_permission` VALUES (1, 2);
INSERT INTO `sys_role_permission` VALUES (3, 2);
INSERT INTO `sys_role_permission` VALUES (1, 3);
INSERT INTO `sys_role_permission` VALUES (5, 3);-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (`userid` int(0) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,`userpwd` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,`sex` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,`address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '張三', '$2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6', '男', '鄭州');
INSERT INTO `sys_user` VALUES (2, '李四', '$2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6', '男', '北京');
INSERT INTO `sys_user` VALUES (3, '王五', '$2a$10$cI7e7bgSs9.9nNHhxKO9LuK/Ll.AeZwgUyZb77oD2y3UwwZyZhWG6', '女', '杭州');-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (`userid` int(0) NOT NULL,`roleid` int(0) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2);
INSERT INTO `sys_user_role` VALUES (3, 3);
INSERT INTO `sys_user_role` VALUES (1, 2);SET FOREIGN_KEY_CHECKS = 1;

第二步:創建一個SpringBoot項目--------引入相關的依賴(lombok依賴? ?mysql驅動依賴? mybatis-plus依賴,security啟動依賴)

 <dependencies><!--加入security安全框架依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--lombok依賴--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql驅動依賴--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis-plus依賴--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>

第三步:創建實體類? ?entity層 (User? Role? Permission)

User實體類

package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("sys_user")   //實體類和數據庫表名不一致時,使用該注解進行映射
public class User {@TableId(type = IdType.AUTO)    //主鍵自增private Integer userid;private String username;private String userpwd;private String sex;private String address;
}

Role實體類

package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("sys_role")    //實體類和數據庫表名不一致時,使用該注解進行映射
public class Role {@TableId(type = IdType.AUTO)    //主鍵自增private Integer roleid;private  String rolename;
}

Permission實體類

package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("sys_permission")    //實體類和數據庫表名不一致時,使用該注解進行映射
public class Permission {@TableId(type = IdType.AUTO)    //主鍵自增private Integer perid;private String pername;private String percode;
}

第四步:創建相應的Dao接口? ?Dao層? (UserDao? RoleDao? PermissionDao)

UserDao接口

package com.aaa.dao;import com.aaa.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserDao extends BaseMapper<User> {}

ReloDao接口

package com.aaa.dao;import com.aaa.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface RoleDao extends BaseMapper<Role> {
}

PermissionDao接口

package com.aaa.dao;import com.aaa.entity.Permission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;import java.util.List;public interface PermissionDao extends BaseMapper<Permission> {//查找用戶的權限@Select("select distinct p.* from sys_user_role u join sys_role_permission r on u.roleid=r.roleid join sys_permission p on r.perid=p.perid where userid=#{userId}")public List<Permission> selectByUserId(Integer userId);}

第五步:創建業務層 Service? ???MyUserDetailService代碼

package com.aaa.service;import com.aaa.dao.PermissionDao;
import com.aaa.dao.UserDao;
import com.aaa.entity.Permission;
import com.aaa.entity.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;@Service
public class MyUserDetailService implements UserDetailsService {@Autowiredprivate UserDao userDao;@Autowiredprivate PermissionDao permissionDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//1. 根據賬戶查找用戶信息QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("username",username);User user = userDao.selectOne(queryWrapper);//2.判斷用戶是否為nullif(user!=null){//3.查找用戶的具體權限List<Permission> permissions = permissionDao.selectByUserId(user.getUserid());/*** String username, 賬戶* String password, 密碼-->而是數據庫中存在的密碼* Collection<? extends GrantedAuthority> authorities 當前用戶具有的權限*/Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();//把List<Permission>轉化為Collection<SimpleGrantedAuthority>for(Permission per:permissions){SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(per.getPercode());authorities.add(simpleGrantedAuthority);}org.springframework.security.core.userdetails.User userDetail=new org.springframework.security.core.userdetails.User(user.getUsername(),user.getUserpwd(),authorities);return userDetail;}return null;}
}

第六步:創建Security配置文件? ?config層? ?mySecurityConfig文件

package com.aaa.config;import com.aaa.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyUserDetailService userDetailService;//添加加密器@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用數據庫賬號和密碼auth.userDetailsService(userDetailService);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//設置表單信息//使用自己的前端登錄頁面http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").successForwardUrl("/success").permitAll();//異常處理頁面http.exceptionHandling().accessDeniedPage("/403.html");//禁用跨域偽造響應http.csrf().disable();//其他所有路徑通過都需要認證http.authorizeRequests().anyRequest().authenticated();}
}

第七步:配置application.properties文件,連接數據庫

server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/security
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

第八步:在SpringSecurity002Application? 啟動類中添加注解讓其掃描到com.aaa.dao層

package com;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
//添加注解  包掃描到dao層@MapperScan(basePackages = "com.aaa.dao")
public class SpringSecurity002Application {public static void main(String[] args) {SpringApplication.run(SpringSecurity002Application.class, args);}}

第九步:訪問資源

使用security認證授權----完成前后端分離的模式

熟悉前后端分離

JWT概述?

1.什么是JWT?

json web token(JWT)是一個開放標準(rfc7519),它定義了一種緊湊的、自包含的方式,用于在各方之間以JSON對象安全地傳輸信息。它是以JSON形式作為Web應用中的令牌,用于在各方之間安全地將信息作為JSON對象傳輸。在數據傳輸過程中還可以完成數據加密、簽名等相關處理。

2.JWT能做什么?

授權
這是使用JWT的最常見方案。一旦用戶登錄,每個后續請求將包括JWT,從而允許用戶訪問該令牌允許的路由,服務和資源。單點登錄是當今廣泛使用JWT的一項功能,因為它的開銷很小并且可以在不同的域中輕松使用。
信息交換
JSON Web Token是在各方之間安全地傳輸信息的好方法。因為可以對JWT進行簽名(例如,使用公鑰/私鑰對),所以您可以確保發件人是他們所說的人。此外,由于簽名是使用標頭和有效負載計算的,因此您還可驗證內容是否遭到篡改。

3.Session認證與JWT認證的區別?

基于傳統的Session認證策略#

a.認證方式#
http協議本身是一種無狀態的協議,而這就意味著如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行。
因為根據http協議,我們并不能知道是哪個用戶發出的請求,所以為了讓我們的應用能識別是哪個用戶發出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息(sessionId)會在響應時傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的基于session認證。
b.Session認證的問題
  • 每個用戶經過我們的應用認證之后,我們的應用都要在服務端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內存中,而隨著認證用戶的增多,服務端的開銷會明顯增大
  • 用戶認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味著用戶下次請求還必須要請求在這臺服務器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味著限制了應用的擴展能力。
  • 因為是基于cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊。
  • 在前后端分離系統中就更加痛苦!也就是說前后端分離在應用解耦后增加了部署的復雜性。
    • 通常用戶一次請求就要轉發多次。如果用session 每次攜帶sessionid 到服務器,服務器還要查詢用戶信息。同時如果用戶很多。這些信息存儲在服務器內存中,給服務器增加負擔。
    • CSRF(跨站偽造請求攻擊)攻擊,session是基于cookie進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊。
    • sessionid 就是一個特征值,表達的信息不夠豐富。不容易擴展。而且如果你后端應用是多節點部署。那么就需要實現session共享機制。 不方便集群應用。

基于JWT認證的策略#

認證流程#
  • 首先,前端通過Web表單將自己的用戶名和密碼發送到后端的接口。這一過程一般是一個HTTP POST請求。建議的方式是通過SSL加密的傳輸(https協議),從而避免敏感信息被嗅探。
  • 后端核對用戶名和密碼成功后,將用戶的id等其他信息作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT(Token)。形成的JWT就是一個形同xxx.yyy.zzz的字符串
    即token = head.payload.singurater
  • 后端將JWT字符串作為登錄成功的返回結果返回給前端。前端可以將返回的結果保存在localStorage或sessionStorage上,退出登錄時前端刪除保存的JWT即可。
  • 前端在每次請求時將JWT放入HTTP Header中的Authorization位。(解決XSS和XSRF問題) HEADER
  • 后端檢查是否存在,如存在驗證JWT的有效性。例如,檢查簽名是否正確;檢查Token是否過期;檢查Token的接收方是否是自己(可選)。
  • 驗證通過后后端使用JWT中包含的用戶信息進行其他邏輯操作,返回相應結果。

4.jwt優勢#

  • 簡潔(Compact): 可以通過URL,POST參數或者在HTTP header發送,因為數據量小,傳輸速度也很快

  • 自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫

  • 因為Token是以JSON加密的形式保存在客戶端的,所以JWT是跨語言的,原則上任何web形式都支持。

  • 不需要在服務端保存會話信息,特別適用于分布式微服務。1.登錄成功----返回json數據

5.JWT的結構#

令牌組成#

  • 1.標頭(Header)
  • 2.有效載荷(Payload)
  • 3.簽名(Signature)

因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz 也就是 Header.Payload.Signature

Header的組成信息#

  • 標頭通常由兩部分組成:令牌的類型(即JWT)和所使用的簽名算法,例如HMAC SHA256或RSA。它會使用 Base64 編碼組成 JWT 結構的第一部分。

  • 注意:Base64是一種編碼,也就是說,它是可以被翻譯回原來的樣子來的。它并不是一種加密過程。

// header的組成信息
{"alg": "HS256","typ": "JWT"
}

Payload組成信息#

令牌的第二部分是有效負載,其中包含聲明。聲明是有關實體(通常是用戶信息)和其他數據的聲明。同樣的,它會使用 Base64 編碼組成 JWT 結構的第二部分

// payload組成信息
{"id": "823","name": "Code Duck","role": "admin"
}

Signature的組成信息#

header和payload都是使用 Base64 進行編碼的,即前端可以解開知道里面的信息。Signature 需要使用編碼后的 header 和 payload 以及我們自己的一個密鑰,然后使用 header 中指定的簽名算法(HS256)進行簽名。簽名的作用是保證 JWT 沒有被篡改過。

例如:HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret );

# 簽名目的
- 最后一步簽名的過程,實際上是對頭部以及負載內容進行簽名,防止內容被竄改。如果有人對頭部以及負載的內容解碼之后進行修改,再進行編碼,最后加上之前的簽名組合形成新的JWT的話,那么服務器端會判斷出新的頭部和負載形成的簽名和JWT附帶上的簽名是不一樣的。如果要對新的頭部和負載進行簽名,在不知道服務器加密時用的密鑰的話,得出來的簽名也是不一樣的。# 信息安全問題
- 在這里大家一定會問一個問題:Base64是一種編碼,是可逆的,那么我的信息不就被暴露了嗎?- 是的。所以,在JWT中,不應該在負載里面加入任何敏感的數據。在上面的例子中,我們傳輸的是用戶的User ID。這個值實際上不是什么敏感內容,一般情況下被知道也是安全的。但是像密碼這樣的內容就不能被放在JWT中了。如果將用戶的密碼放在了JWT中,那么懷有惡意的第三方通過Base64解碼就能很快地知道你的密碼了。因此JWT適合用于向Web應用傳遞一些非敏感信息。JWT還經常用于設計用戶認證和授權系統,甚至實現Web應用的單點登錄。

以上三部分進行整合#

JWT的真實面目:

(Header)eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

(payLoad)eyJyb2xlIjoiZW1wbG95ZWUiLCJpZCI6IjQiLCJleHAiOjE1OTcxMjc3NjUsInVzZXJuYW1lIjoiamFzb24ifQ.

(Signature)WxIiTf7V4UaboMONu0UpPu-uQSuDQFZqepKKxLstnaU

1.登錄成功---返回json數據

2.登錄失敗----返回json數據

3.權限不足----返回json數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696183.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696183.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696183.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

指針筆試題(C語言進階)

目錄 前言 1、案例一 1.1 答案 1.2 解析 2、案例二 2.1 答案 2.2 解析 3、案例三 3.1 答案 3.2 解析 4、案例四 4.1 答案 4.2 解析 5、案例五 5.1 答案 5.2 解析 總結 前言 “紙上得來終覺淺&#xff0c;絕知此事要躬行”。本篇通過對指針實際案例的分析&…

Google重磅開源!Gemma 2B/7B小模型登場,6萬億Tokens喂飽,聊天編程兩不誤,LLaMA也黯然失色?

Google又有大動作&#xff01; 近日&#xff0c;他們發布了Gemma 2B和7B兩個開源AI模型&#xff0c;與大型封閉模型不同&#xff0c;它們更適合小型任務&#xff0c;如聊天和文本摘要。 這兩個模型在訓練過程中使用了6萬億個Tokens的數據&#xff0c;包括網頁文檔、代碼和數學…

收單外包機構備案2023年回顧和2024年展望

孟凡富 本文原標題為聚合支付深度復盤與展望&#xff0c;首發于《支付百科》公眾號&#xff01; 收單外包服務機構在我國支付收單市場中占據著舉足輕重的地位&#xff0c;其規模在政策引導和市場需求驅動下不斷擴大。同時&#xff0c;隨著行業自律管理體系的持續發展和完善&a…

文獻速遞:GAN醫學影像合成--用生成對抗網絡生成 3D TOF-MRA 體積和分割標簽

文獻速遞&#xff1a;GAN醫學影像合成–用生成對抗網絡生成 3D TOF-MRA 體積和分割標簽 01 文獻速遞介紹 深度學習算法在自然圖像分析中的成功近年來已被應用于醫學成像領域。深度學習方法已被用于自動化各種耗時的手動任務&#xff0c;如醫學圖像的分割和分類&#xff08;G…

頂刊中很出彩的二元變量圖

導師希望你發頂刊, 但你的圖紙差點意思, 那么,你不妨試試這個, 二元變量圖, 在頂刊中都很出彩哦! 本次,我們來以“降水量”和“NDVI”兩個數據為例,繪制二元變量分析圖,表達“降水量”和“NDVI”之間的關系。 什么是二元變量圖 首先還是先解釋下“二元變量圖”。顧…

OpenCV中saturate_cast模板函數

在OpenCV中&#xff0c;saturate_cast是一個模板函數&#xff0c;用于正確地將一個數值從一種類型轉換到另一種類型&#xff0c;同時確保結果在目標類型的有效范圍內。這在圖像處理中特別有用&#xff0c;比如當像素值在經過計算后可能超出其數據類型允許的范圍時。saturate_ca…

-bash: /root/.ssh/authorized_keys: Read-only file system

問題背景 由于跳板機不支持 ssh-copy-id 命令&#xff0c;為了配置免密登錄&#xff0c;考慮在服務器上手動使用 cat 命令寫入跳板機公鑰 cat <<EOL >> ~/.ssh/authorized_keys [Your public key] EOL但卻出現了以下錯誤 -bash: /root/.ssh/authorized_keys: Re…

編程筆記 Golang基礎 013 格式化輸入輸出

編程筆記 Golang基礎 013 格式化輸入輸出 一、格式化輸出1. fmt.Print系列函數2. Printf格式說明3. 格式化布爾類型 二、格式化輸入1. fmt.Scan系列函數注意事項 三、練習小結 Go語言中的格式化輸入和輸出主要通過標準庫 fmt 包來實現。主要是輸出需要格式化。 一、格式化輸出 …

掃盲貼:Svg動畫和Canvas動畫有什么區別

hello&#xff0c;我是貝格前端工場&#xff0c;網頁中動畫的實現有N種方式&#xff0c;比如css動畫&#xff0c;js動畫&#xff0c;svg動畫&#xff0c;canvas動畫等等&#xff0c;每一種動畫都有對應的場景&#xff0c;本問重點介紹一下svg和canvas動畫的異同點&#xff0c;歡…

大工程 從0到1 數據治理 數倉篇(sample database classicmodels _No.7)

大工程 從0到1 數據治理 之數倉篇 我這里還是sample database classicmodels為案列&#xff0c;可以下載&#xff0c;我看 網上還沒有類似的 案列&#xff0c;那就 從 0-1開始吧&#xff01; 提示&#xff1a;寫完文章后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參…

TRS 2024 論文閱讀 | 基于點云處理和點Transformer網絡的人體活動連續識別

無線感知/雷達成像部分最新工作<持續更新>: 鏈接地址 注1:本文系“無線感知論文速遞”系列之一,致力于簡潔清晰完整地介紹、解讀無線感知領域最新的頂會/頂刊論文(包括但不限于 Nature/Science及其子刊; MobiCom, Sigcom, MobiSys, NSDI, SenSys, Ubicomp; JSAC, 雷達學…

提高代碼質量的 10 條編碼原則

提高代碼質量的 10 條編碼原則 本文轉自 公眾號 ByteByteGo&#xff0c;如有侵權&#xff0c;請聯系&#xff0c;立即刪除 今天來聊聊提高代碼質量的 10 條編碼原則。 軟件開發需要良好的系統設計和編碼標準。我們在下圖中列出了 10 條良好的編碼原則。 01 遵循代碼規范 我們…

Studio One破解版和正版的區別 Studio One購買是永久的嗎

在過去的很長一段時間里&#xff0c;很多小伙伴想要使用一款軟件時&#xff0c;可能第一時間就去網上尋找破解版的資源&#xff0c; 白嫖的資源固然很香&#xff0c;但隨著法制的健全和人們版權意識的增強&#xff0c;現在破解版的資源是越來越少了。同時破解版的資源也會伴隨著…

大數據計算技術秘史(上篇)

在之前的文章《2024 年&#xff0c;一個大數據從業者決定……》《存儲技術背后的那些事兒》中&#xff0c;我們粗略地回顧了大數據領域的存儲技術。在解決了「數據怎么存」之后&#xff0c;下一步就是解決「數據怎么用」的問題。 其實在大數據技術興起之前&#xff0c;對于用戶…

react實現拖拽的插件

插件一&#xff1a;dnd-kit 插件官網鏈接https://docs.dndkit.com/introduction/installation 插件二&#xff1a;react-beautiful-dnd https://github.com/atlassian/react-beautiful-dnd/tree/master 兩個插件的區別&#xff1a; 插件一可以做到從區域A拖住到區域B 插件二…

平臺組成-門戶服務

整體是從用戶視角&#xff0c;從外往內介紹。前面講了平臺的幾個模塊&#xff08;就是前端部署包&#xff09;。今天開始介紹后臺服務&#xff08;微服務&#xff0c;SpringCloud&#xff0c;通過-jar 參數啟動的jar包&#xff09;。 門戶服務完成登錄、注冊、界面配置、功能鏈…

java面試JVM虛擬機篇

1 JVM組成 1.1 JVM由那些部分組成&#xff0c;運行流程是什么&#xff1f; 難易程度&#xff1a;☆☆☆ 出現頻率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的運行環境&#xff08;java二進制字節碼的運行環境&#xff09; 好處&#xff1a; 一次編寫&a…

Go 原子操作有哪些?

Go atomic包是最輕量級的鎖&#xff08;也稱無鎖結構&#xff09;&#xff0c;可以在不形成臨界區和創建互斥量的情況下完成并發安全的值替換操作&#xff0c;不過這個包只支持int32/int64/uint32/uint64/uintptr這幾種數據類型的一些基礎操作&#xff08;增減、交換、載入、存…

Excel之index、MATCH面試題、VLOOKUP函數,

VLOOKUP() 在表格的首列查找指定的數值&#xff0c;并返回表格當前行中指定列處的數值。 結構&#xff1a;VLOOKUP(查找值,查找區域,列序數,匹配條件) 解釋&#xff1a;VLOOKUP(找誰,在哪里找,第幾列,0或1) 1.目的&#xff1a;根據【產品】查找【銷量】 公式&#xff1a;V…

pikachu靶場-XSS

XSS&#xff1a; XSS&#xff08;跨站腳本&#xff09;概述 Cross-Site Scripting 簡稱為“CSS”&#xff0c;為避免與前端疊成樣式表的縮寫"CSS"沖突&#xff0c;故又稱XSS。一般XSS可以分為如下幾種常見類型&#xff1a; 1.反射性XSS; 2.存儲型XSS; 3.DOM型XSS; …