SpringSecurity 應(yīng)用
簡介
Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉(zhuǎn)Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少了為企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。
認(rèn)證授權(quán)分析
用戶在進(jìn)行資源訪問時(shí),要求系統(tǒng)要對用戶進(jìn)行權(quán)限控制,其具體流程如圖-1所示:
SpringSecurity 架構(gòu)設(shè)計(jì)
鳥瞰SpringSecurity 基本技術(shù)架構(gòu),例如:
綠色部分是認(rèn)證過濾器,需要我們自己配置,可以配置多個(gè)認(rèn)證過濾器。認(rèn)證過濾器可以使用 Spring Security 提供的認(rèn)證過濾器,也可以自定義過濾器(例如:短信驗(yàn)證)。認(rèn)證過濾器要在 configure(HttpSecurity http)方法中配置,沒有配置不生效。下面會(huì)重點(diǎn)介紹以下三個(gè)過濾器:
UsernamePasswordAuthenticationFilter 過濾器:該過濾器會(huì)攔截前端提交的 POST 方式的登錄表單請求,并進(jìn)行身份認(rèn)證。
BasicAuthenticationFilter:檢測和處理 http basic 認(rèn)證。
ExceptionTranslationFilter 過濾器:該過濾器不需要我們配置,對于前端提交的請求會(huì)直接放行,捕獲后續(xù)拋出的異常并進(jìn)行處理(例如:權(quán)限訪問限制)。
FilterSecurityInterceptor 過濾器:該過濾器是過濾器鏈的最后一個(gè)過濾器,根據(jù)資源權(quán)限配置來判斷當(dāng)前請求是否有權(quán)限訪問對應(yīng)的資源。如果訪問受限會(huì)拋出相關(guān)異常,并由 ExceptionTranslationFilter 過濾器進(jìn)行捕獲和處理。
快速入門實(shí)踐
創(chuàng)建項(xiàng)目
創(chuàng)建security項(xiàng)目,其pom.xml文件內(nèi)容如下:
<?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> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.3.2.RELEASE</version> </parent> <groupId>com.cy</groupId> <artifactId>05-security</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project>
添加項(xiàng)目依賴
第一步:創(chuàng)建項(xiàng)目,其pom.xml文件核心依賴如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>
第二步:啟動(dòng)服務(wù)(依賴添加以后會(huì)默認(rèn)添加一個(gè)tomcat,端口8080)
服務(wù)啟動(dòng)之后,你會(huì)發(fā)現(xiàn),控制臺(tái)會(huì)出現(xiàn)一個(gè)隨機(jī)的密碼,用于訪問當(dāng)前系統(tǒng),默認(rèn)用戶名是user,密碼就是控制臺(tái)上的密碼,如圖所示:
啟動(dòng)服務(wù)訪問測試
服務(wù)啟動(dòng)后,打開瀏覽器進(jìn)行訪問,如圖所示:
輸入賬號(默認(rèn)用戶名為user)和密碼登陸成功默認(rèn)為如下頁面.
其中,出現(xiàn)這個(gè)頁面表示還沒有配置登陸成功頁面,這個(gè)資源頁面現(xiàn)在還不存在,可以在項(xiàng)目的resources目錄下創(chuàng)建static目錄(假如沒有此目錄),然后在此目錄下創(chuàng)建index.html頁面,內(nèi)容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <h1>The Index Page</h1> </div> </body> </html>
此時(shí),再次啟動(dòng)服務(wù)進(jìn)行登陸,呈現(xiàn)登陸成功的效果,如圖所示:
自定義認(rèn)證邏輯
認(rèn)證流程分析
定義security配置類
定義配置類,基于此類配置認(rèn)證和授權(quán)邏輯,例如:
package com.cy.security.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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 public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 認(rèn)證授權(quán)管理器對用戶輸入的密碼與數(shù)據(jù)庫中存儲(chǔ)的密碼進(jìn)行比對時(shí), * 需要對這個(gè)密碼加密,加密算法需要我們自己指定 * @return */ @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); //關(guān)閉跨域攻擊 http.csrf().disable(); //自定義登陸表單 http.formLogin().loginPage("/login.html").loginProcessingUrl("/login"); //請求資源的認(rèn)證配置 http.authorizeRequests() .antMatchers("/login","/login.html") .permitAll() .anyRequest().authenticated(); } }
定義數(shù)據(jù)訪問層對象
定義數(shù)據(jù)訪問層對象,基于此對象實(shí)現(xiàn)用戶及用戶權(quán)限信息的獲取,例如:
package com.cy.security.dao; import com.cy.security.domain.SysUser; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface UserMapper { /** * 基于用戶名獲取用戶信息 * @param username * @return */ @Select("select id,username,password,status " + "from tb_users " + "where username=#{username}") SysUser selectUserByUsername(String username); /** * 基于用戶id查詢用戶權(quán)限 * @param userId 用戶id * @return 用戶的權(quán)限 * 涉及到的表:tb_user_roles,tb_role_menus,tb_menus */ @Select("select distinct m.permission " + "from tb_user_roles ur join tb_role_menus rm on ur.role_id=rm.role_id" + " join tb_menus m on rm.menu_id=m.id " + "where ur.user_id=#{userId} and m.permission is not null") List<String> selectUserPermissions(Long userId); }
定義UserDetailService接口實(shí)現(xiàn)類
Spring Security 提供了一個(gè)UserDetailService接口,我們可以基于此接口實(shí)現(xiàn)類,實(shí)現(xiàn)用戶信息的獲取和封裝,例如:
@Service public class UserDetailServiceImpl implements UserDetailsService { @Autowired private UserMapper userMapper; /** * 客戶端點(diǎn)擊登陸時(shí),添加些用戶信息會(huì)傳給此方法 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //1.基于用戶名查詢用戶信息 SysUser user=userMapper.selectUserByUsername(username); //...判斷自己寫... System.out.println(user); //2.查詢用戶登陸用戶權(quán)限 List<String> permissions=userMapper.selectUserPermissions(user.getId()); System.out.println(permissions); //3.封裝用戶信息并返回,將用戶信息交給認(rèn)證管理器,認(rèn)證授權(quán)管理器負(fù)責(zé)對用戶輸入的信息進(jìn)行認(rèn)證和授權(quán) List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(permissions.toArray(new String[]{})); return new User(username, user.getPassword(),authorityList); } }
自定義登陸頁面
在resources的static目錄下創(chuàng)建login.html頁面,例如:
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="external nofollow" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>login</title> </head> <body> <div class="container"id="app"> <h3>Please Login</h3> <form> <div class="mb-3"> <label for="usernameId" class="form-label">Username</label> <input type="text" v-model="username" class="form-control" id="usernameId" aria-describedby="emailHelp"> </div> <div class="mb-3"> <label for="passwordId" class="form-label">Password</label> <input type="password" v-model="password" class="form-control" id="passwordId"> </div> <button type="button" @click="doLogin()" class="btn btn-primary">Submit</button> </form> </div> <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> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var vm=new Vue({ el:"#app",//定義監(jiān)控點(diǎn),vue底層會(huì)基于此監(jiān)控點(diǎn)在內(nèi)存中構(gòu)建dom樹 data:{ //此對象中定義頁面上要操作的數(shù)據(jù) username:"", password:"" }, methods: {//此位置定義所有業(yè)務(wù)事件處理函數(shù) doLogin() { //1.定義url let url = "http://localhost:8080/login" //2.定義參數(shù) let params = new URLSearchParams() params.append("username",this.username); params.append("password",this.password); debugger //3.發(fā)送異步請求 axios.post(url, params).then((response) => { alert("login ok"); location.href="/index.html" rel="external nofollow" }) } } }); </script> </body> </html>
啟動(dòng)服務(wù)進(jìn)行訪問測試
授權(quán)邏輯設(shè)計(jì)及實(shí)現(xiàn)
修改授權(quán)配置類
在權(quán)限配置類上添加啟用全局方法訪問控制注解,例如
package com.cy.auth.config; //這個(gè)配置類是配置Spring-Security的, //prePostEnabled= true表示啟動(dòng)權(quán)限管理功能 @EnableGlobalMethodSecurity(prePostEnabled = true) @Configuration public class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter { …… }
定義資源訪問對象
package com.cy.res.controller; @RequestMapping("/res") @RestController public class ResourceController { @PreAuthorize("hasAuthority("sys:res:view")") @RequestMapping("/retrieve") public String doRetrieve(){ return "select resource ok"; } @PreAuthorize("hasAuthority("sys:res:create")") @RequestMapping("/create") public String doCreate(){ return "create resource"; } }
其中,@PreAuthorize注解描述方法時(shí),用于告訴系統(tǒng)訪問此方法時(shí)需要進(jìn)行權(quán)限檢測。需要具備指定權(quán)限才可以訪問。例如:
啟動(dòng)服務(wù)實(shí)現(xiàn)訪問測試
打開瀏覽器分別輸入http://localhost:8080/res/create和http://localhost:8080/res/select進(jìn)行測試分析。
總結(jié)(Summary)
本章節(jié)主要是對spring security 在springboot平臺(tái)下是如何使用的。
到此這篇關(guān)于SpringBoot工程中Spring Security應(yīng)用實(shí)踐的文章就介紹到這了,更多相關(guān)Spring Security應(yīng)用實(shí)踐內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/maitian_2008/article/details/120191729