java編程SpringSecurity入門原理及應(yīng)用簡介
1. SpringSecurity 框架簡介
1.1 概要
正如你可能知道的關(guān)于安全方面的兩個(gè)主要區(qū)域是“認(rèn)證”和“授權(quán)”(或者訪問控制),一般來說,Web 應(yīng)用的安全性包括用戶認(rèn)證(Authentication)和用戶授權(quán)(Authorization)兩個(gè)部分,這兩點(diǎn)也是 Spring Security 重要核心功能。
(1) 用戶認(rèn)證指的是:驗(yàn)證某個(gè)用戶是否為系統(tǒng)中的合法主體,也就是說用戶能否訪問該系統(tǒng)。用戶認(rèn)證一般要求用戶提供用戶名和密碼。系統(tǒng)通過校驗(yàn)用戶名和密碼來完成認(rèn)證過程。通俗點(diǎn)說就是系統(tǒng)認(rèn)為用戶是否能登錄
(2) 用戶授權(quán)指的是驗(yàn)證某個(gè)用戶是否有權(quán)限執(zhí)行某個(gè)操作。在一個(gè)系統(tǒng)中,不同用戶所具有的權(quán)限是不同的。比如對(duì)一個(gè)文件來說,有的用戶只能進(jìn)行讀取,而有的用戶可以進(jìn)行修改。一般來說,系統(tǒng)會(huì)為不同的用戶分配不同的角色,而每個(gè)角色則對(duì)應(yīng)一系列的權(quán)限。通俗點(diǎn)講就是系統(tǒng)判斷用戶是否有權(quán)限去做某些事情。
1.2 組成以及同款產(chǎn)品(shiro)對(duì)比
1.2.1 Spring Security
SpringSecurity 特點(diǎn):
- 和 Spring 無縫整合。
- 全面的權(quán)限控制。
- 專門為Web 開發(fā)而設(shè)計(jì)。
- 舊版本不能脫離Web 環(huán)境使用。
- 新版本對(duì)整個(gè)框架進(jìn)行了分層抽取,分成了核心模塊和Web 模塊。單獨(dú)引入核心模塊就可以脫離Web 環(huán)境。
- 重量級(jí)。
1.2.2 Shiro
Apache 旗下的輕量級(jí)權(quán)限控制框架。
特點(diǎn):
- 輕量級(jí)。Shiro 主張的理念是把復(fù)雜的事情變簡單。針對(duì)對(duì)性能有更高要求的互聯(lián)網(wǎng)應(yīng)用有更好表現(xiàn)。
- 通用性。
好處:不局限于Web 環(huán)境,可以脫離Web 環(huán)境使用。
缺陷:在Web 環(huán)境下一些特定的需求需要手動(dòng)編寫代碼定制。
Spring Security 是 Spring 家族中的一個(gè)安全管理框架,實(shí)際上,在 Spring Boot 出現(xiàn)之前,Spring Security 就已經(jīng)發(fā)展了多年了,但是使用的并不多,安全管理這個(gè)領(lǐng)域,一直是 Shiro 的天下。
相對(duì)于 Shiro,在 SSM 中整合 Spring Security 都是比較麻煩的操作,所以,SpringSecurity 雖然功能比 Shiro 強(qiáng)大,但是使用反而沒有 Shiro 多(Shiro 雖然功能沒有Spring Security 多,但是對(duì)于大部分項(xiàng)目而言,Shiro 也夠用了)。
自從有了 Spring Boot 之后,Spring Boot 對(duì)于 Spring Security 提供了自動(dòng)化配置方案,可以使用更少的配置來使用 Spring Security。
因此,一般來說,常見的安全管理技術(shù)棧的組合是這樣的:
SSM + Shiro
Spring Boot/Spring Cloud + Spring Security
以上只是一個(gè)推薦的組合而已,如果單純從技術(shù)上來說,無論怎么組合,都是可以運(yùn)行的。
1.3 模塊劃分
1.4 SpringSecurity 基本原理
SpringSecurity 本質(zhì)是一個(gè)過濾器鏈: 從啟動(dòng)是可以獲取到過濾器鏈:
代碼底層流程:重點(diǎn)看三個(gè)過濾器:
FilterSecurityInterceptor:是一個(gè)方法級(jí)的權(quán)限過濾器, 基本位于過濾鏈的最底部。
1.5.UserDetailsService 接口講解
當(dāng)什么也沒有配置的時(shí)候,賬號(hào)和密碼是由 Spring Security 定義生成的。而在實(shí)際項(xiàng)目中
賬號(hào)和密碼都是從數(shù)據(jù)庫中查詢出來的。 所以我們要通過自定義邏輯控制認(rèn)證邏輯。
如果需要自定義邏輯時(shí),只需要實(shí)現(xiàn)userDetailsService接口即可,定義如下:
2.SpringSecurity Web 權(quán)限方案
2.1設(shè)置登錄系統(tǒng)的賬號(hào)密碼(三種方式)
一:在application.xml中自行配置
spring.security.user.name = xxx
spring.security.user.password = xxx
二:編寫類實(shí)現(xiàn)接口
三:實(shí)現(xiàn)數(shù)據(jù)庫認(rèn)證來完成用戶登錄
這里就拿一個(gè)例子來完成認(rèn)證和授權(quán)
設(shè)計(jì)數(shù)據(jù)庫表
建立springboot項(xiàng)目,勾選相應(yīng)依賴
完整pom.xml
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.9</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> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
數(shù)據(jù)庫配置
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource username: root password: xxxxxx url: jdbc:mysql://xxxxxxxxxx //根據(jù)自己情況填寫
創(chuàng)建對(duì)應(yīng)的實(shí)體類
package com.example.demo.domain; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class User implements UserDetails { private Integer id; private String username; private String password; private Boolean enabled; private Boolean locked; private List<Role> roles; @Override // 實(shí)體類和SpringSecurity轉(zhuǎn)換 public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } @Override public String getPassword() { return null; } @Override public String getUsername() { return null; } @Override public boolean isAccountNonExpired() { return false; } @Override public boolean isAccountNonLocked() { return false; } @Override public boolean isCredentialsNonExpired() { return false; } @Override public boolean isEnabled() { return false; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getLocked() { return locked; } public void setLocked(Boolean locked) { this.locked = locked; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } }
package com.example.demo.domain; public class Role { private Integer id; private String name; private String nameZh; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNameZh() { return nameZh; } public void setNameZh(String nameZh) { this.nameZh = nameZh; } }
創(chuàng)建service層
package com.example.demo.mapper; import com.example.demo.domain.Role; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.graalvm.compiler.nodeinfo.StructuralInput; import org.springframework.security.core.userdetails.User; import java.util.List; @Mapper public interface UserMapper { @Select("select * from user where username=#{username}") public User loadUserByUsername(String username); @Select("select * from role r, user_role ur where r.id = ur.rid and ur.uid = #{id}") public List<Role> getUserRoleByUid(Integer id); }
配置spring security
package com.example.demo.config; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.context.annotation.Bean; 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; @Configurable public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login").permitAll() .and() .csrf().disable(); } }
security配置類說明
3.security-記住我-的實(shí)現(xiàn)
在security配置類中開啟remember me功能,并設(shè)置有效期。默認(rèn)時(shí)間為兩周。
4.用戶注銷功能實(shí)現(xiàn)
通過退出按鈕找到映射地址,在配置類中添加退出映射地址
5.關(guān)于CSRF
跨站請(qǐng)求偽造(英語:Cross-site request forgery),也被稱為 one-click
attack 或者 session riding,通常縮寫為 CSRF 或者 XSRF, 是一種挾制用戶在當(dāng)前已登錄的 Web 應(yīng)用程序上執(zhí)行非本意的操作的攻擊方法。跟跨網(wǎng)站腳本(XSS)相比,XSS 利用的是用戶對(duì)指定網(wǎng)站的信任,CSRF 利用的是網(wǎng)站對(duì)用戶網(wǎng)頁瀏覽器的信任。
跨站請(qǐng)求攻擊,簡單地說,是攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個(gè)自己曾經(jīng)認(rèn)證過的網(wǎng)站并運(yùn)行一些操作(如發(fā)郵件,發(fā)消息,甚至財(cái)產(chǎn)操作如轉(zhuǎn)賬和購買商品)。由于瀏覽器曾經(jīng)認(rèn)證過,所以被訪問的網(wǎng)站會(huì)認(rèn)為是真正的用戶操作而去運(yùn)行。這利用了web 中用戶身份驗(yàn)證的一個(gè)漏洞:簡單的身份驗(yàn)證只能保證請(qǐng)求發(fā)自某個(gè)用戶的瀏覽器,卻不能保證請(qǐng)求本身是用戶自愿發(fā)出的。
從 Spring Security 4.0 開始,默認(rèn)情況下會(huì)啟用CSRF 保護(hù),以防止CSRF 攻擊應(yīng)用程序,Spring Security CSRF 會(huì)針對(duì) PATCH,POST,PUT 和DELETE 方法進(jìn)行防護(hù)。
6.spring security原理總結(jié)
SpringSecurity 采用的是責(zé)任鏈的設(shè)計(jì)模式,它有一條很長的過濾器鏈。
現(xiàn)在對(duì)這條過濾器鏈的 15 個(gè)過濾器進(jìn)行說明:
(1) WebAsyncManagerIntegrationFilter:將 Security 上下文與 Spring Web 中用于處理異步請(qǐng)求映射的 WebAsyncManager 進(jìn)行集成。
(2) SecurityContextPersistenceFilter:在每次請(qǐng)求處理之前將該請(qǐng)求相關(guān)的安全上下文信息加載到 SecurityContextHolder 中,然后在該次請(qǐng)求處理完成之后,將
SecurityContextHolder 中關(guān)于這次請(qǐng)求的信息存儲(chǔ)到一個(gè)“倉儲(chǔ)”中,然后將
SecurityContextHolder 中的信息清除,例如在 Session 中維護(hù)一個(gè)用戶的安全信息就是這個(gè)過濾器處理的。
(3) HeaderWriterFilter:用于將頭信息加入響應(yīng)中。
(4) CsrfFilter:用于處理跨站請(qǐng)求偽造。
(5) LogoutFilter:用于處理退出登錄。
(6) UsernamePasswordAuthenticationFilter:用于處理基于表單的登錄請(qǐng)求,從表單中獲取用戶名和密碼。默認(rèn)情況下處理來自 /login 的請(qǐng)求。從表單中獲取用戶名和密碼時(shí),默認(rèn)使用的表單 name 值為 username 和 password,這兩個(gè)值可以通過設(shè)置這個(gè)過濾器的 usernameParameter 和 passwordParameter 兩個(gè)參數(shù)的值進(jìn)行修改。
(7) DefaultLoginPageGeneratingFilter:如果沒有配置登錄頁面,那系統(tǒng)初始化時(shí)就會(huì)配置這個(gè)過濾器,并且用于在需要進(jìn)行登錄時(shí)生成一個(gè)登錄表單頁面。
(8) BasicAuthenticationFilter:檢測(cè)和處理 http basic 認(rèn)證。
(9) RequestCacheAwareFilter:用來處理請(qǐng)求的緩存。
(10) SecurityContextHolderAwareRequestFilter:主要是包裝請(qǐng)求對(duì)象 request。
(11) AnonymousAuthenticationFilter:檢測(cè) SecurityContextHolder 中是否存在
Authentication 對(duì)象,如果不存在為其提供一個(gè)匿名 Authentication。
(12) SessionManagementFilter:管理 session 的過濾器
(13) ExceptionTranslationFilter:處理 AccessDeniedException 和
AuthenticationException 異常。
(14) FilterSecurityInterceptor:可以看做過濾器鏈的出口。
(15) RememberMeAuthenticationFilter:當(dāng)用戶沒有登錄而直接訪問資源時(shí), 從 cookie 里找出用戶的信息, 如果 Spring Security 能夠識(shí)別出用戶提供的 remember me cookie, 用戶將不必填寫用戶名和密碼, 而是直接登錄進(jìn)入系統(tǒng),該過濾器默認(rèn)不開啟。
以上就是java編程入門SpringSecurity原理及應(yīng)用簡介的詳細(xì)內(nèi)容,更多關(guān)于java編程SpringSecurity簡介的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
jvm內(nèi)存溢出解決方法(jvm內(nèi)存溢出怎么解決)
jvm內(nèi)存溢出解決方法,詳細(xì)內(nèi)容看下面解釋2013-12-12eclipse創(chuàng)建springboot項(xiàng)目的三種方式總結(jié)
這篇文章主要介紹了eclipse創(chuàng)建springboot項(xiàng)目的三種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07詳解大數(shù)據(jù)處理引擎Flink內(nèi)存管理
Flink是jvm之上的大數(shù)據(jù)處理引擎,jvm存在java對(duì)象存儲(chǔ)密度低、full gc時(shí)消耗性能,gc存在stw的問題,同時(shí)omm時(shí)會(huì)影響穩(wěn)定性。針對(duì)頻繁序列化和反序列化問題flink使用堆內(nèi)堆外內(nèi)存可以直接在一些場(chǎng)景下操作二進(jìn)制數(shù)據(jù),減少序列化反序列化消耗。本文帶你詳細(xì)理解其原理。2021-05-05java后端返回?cái)?shù)據(jù)給前端時(shí)去除值為空或NULL的屬性、忽略某些屬性代碼示例
在Java開發(fā)中我們處理JSON數(shù)據(jù)時(shí)經(jīng)常會(huì)遇到空值(null)的情況,這篇文章主要給大家介紹了關(guān)于java后端返回?cái)?shù)據(jù)給前端時(shí)去除值為空或NULL的屬性、忽略某些屬性的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07Java通過反射機(jī)制動(dòng)態(tài)設(shè)置對(duì)象屬性值的方法
下面小編就為大家?guī)硪黄狫ava通過反射機(jī)制動(dòng)態(tài)設(shè)置對(duì)象屬性值的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-07-07spring中@ComponentScan自動(dòng)掃描并指定掃描規(guī)則
本文主要介紹了spring中@ComponentScan自動(dòng)掃描并指定掃描規(guī)則,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Java數(shù)組轉(zhuǎn)換為集合的相關(guān)方法
在Java中我們經(jīng)常需要將數(shù)組從一種類型轉(zhuǎn)換為另一種類型,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)組轉(zhuǎn)換為集合的相關(guān)方法,文中通過圖文及代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01springboot學(xué)習(xí)之Thymeleaf模板引擎及原理介紹
本文主要介紹一下SpringBoot給我們推薦的Thymeleaf模板引擎,這模板引擎呢,是一個(gè)高級(jí)語言的模板引擎,他的這個(gè)語法更簡單而且功能更強(qiáng)大,對(duì)springboot?Thymeleaf模板引擎相關(guān)知識(shí)感興趣的朋友一起看看吧2022-02-02基于Java實(shí)現(xiàn)計(jì)數(shù)排序,桶排序和基數(shù)排序
這篇文章主要為大家詳細(xì)介紹了計(jì)數(shù)排序,桶排序和基數(shù)排序的多種語言的實(shí)現(xiàn)(JavaScript、Python、Go語言、Java),感興趣的小伙伴可以了解一下2022-12-12