1. spring boot框架中怎樣訪問jpa介面中的方法
blic interface UserRepository extends JpaRepository<User, Long> {
User findByName(String name);
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
我們只需要通過編寫一個繼承自 JpaRepository 的介面就能完成數據訪問,下面以一個具體實例來體驗Spring-data-jpa給我們帶來的強大功能。
使用示例
由於Spring-data-jpa依賴於Hibernate。如果您對Hibernate有一定了解,下
2. Spring Security 介面認證鑒權入門實踐指南
Web API 介面服務場景里,用戶的認證和鑒權是很常見的需求,Spring Security 據說是這個領域里事實上的標准,實踐下來整體設計上確實有不少可圈可點之處,也在一定程度上印證了小伙們經常提到的 「太復雜了」 的說法也是很有道理的。
本文以一個簡單的 SpringBoot Web 以應用為例,重點介紹以下內容:
創建 SpringBoot 示例,用於演示 Spring Security 在 SpringBoot 環境下的應用,簡要介紹四部分內容:pom.xml、application.yml、IndexController 和 HelloController。
boot-example 是用於演示的 SpringBoot 項目子模塊(Mole)。
SpringBoot 應用名稱為 example,實例埠為 9999 。
IndexController 實現一個介面:/。
HelloController 實現兩個介面:/hello/world 和 /hello/name。
編譯啟動 SpringBoot 應用,通過瀏覽器請求介面,請求路徑和響應結果:
SpringBoot 示例准備完成。
SpringBoot 集成 Spring Security 僅需要在 pom.xml 中添加相應的依賴: spring-boot-starter-security ,如下:
編譯啟動應用,相對於普通的 SpringBoot 應用,我們可以在命令行終端看到 特別 的兩行日誌:
表示 Spring Security 已在 SpringBoot 應用中生效。 默認 情況下,Spring Security 自動化 地幫助我們完成以下三件事件:
使用 Spring Security 默認為我們生成的用戶名和密碼進行登錄(Sign in),成功之後會自動重定向至 / :
之後我們就可以通過瀏覽器正常請求 /hello/world 和 /hello/name。
默認情況下,Spring Security 僅支持基於 FormLogin 方式的認證,只能使用固定的用戶名和隨機生成的密碼,且不支持鑒權。如果想要使用更豐富的安全特性:
本文以 Java Configuration 的方式為例進行介紹,需要我們提供一個繼承自 WebSecurityConfigurerAdapter 配置類,然後通過重寫若干方法進而實現自定義配置。
SecurityConfig 使用 @Configuration 註解(配置類),繼承自 WebSecurityConfigurerAdapter ,本文通過重寫 configure 方法實現自定義配置。
需要注意: WebSecurityConfigurerAdapter 中有多個名稱為 configure 重載方法,這里使用的是參數類型為 HttpSecurity 的方法。
註:Spring Security 默認自動化配置參考 Spring Boot Auto Configuration 。
用以指定哪些請求需要什麼樣的認證或授權,這里使用 anyRequest() 和 authenticated() 表示所有的請求均需要認證。
表示我們使用 HttpBasic 認證。
編譯啟動應用,會發現終端仍會輸出密碼:
因為,我們僅僅改變的是認證方式。
為方便演示,我們使用 CURL 直接請求介面:
會提示我們 Unauthorized ,即:沒有認證。
我們按照 HttpBasic 要求添加請求頭部參數 Authorization ,它的值:
即:
再次請求介面:
認證成功,介面正常響應。
使用默認用戶名和隨機密碼的方式不夠靈活,大部分場景都需要我們支持多個用戶,且分別為他們設置相應的密碼,這就涉及到兩個問題:
對於 讀取 ,Spring Security 設計了 UserDetailsService 介面:
實現按照用戶名(username)從某個存儲介質中載入相對應的用戶信息(UserDetails)。
用戶名,客戶端發送請求時寫入的用於用戶名。
用戶信息,包括用戶名、密碼、許可權等相關信息。
注意:用戶信息不只用戶名和用戶密碼。
對於 存儲 ,Spring Security 設計了 UserDetailsManager 介面:
創建用戶信息
修改用戶信息
刪除用戶信息
修改當前用戶的密碼
檢查用戶是否存在
注意: UserDetailsManager 繼承自 UserDetailsService 。
也就是說,我們可以通過提供一個已實現介面 UserDetailsManager * 的類,並重寫其中的若干方法,基於某種存儲介質,定義用戶名、密碼等信息的存儲和讀取邏輯;然後將這個類的實例以 Bean 的形式注入 Spring Security,就可以實現用戶名和密碼的自定義。
實際上,Spring Security 僅關心如何 讀取 , 存儲 可以由業務系統自行實現;相當於,只實現介面 UserDetailsService 即可。
Spring Security 已經為我們預置了兩種常見的存儲介質實現:
InMemoryUserDetailsManager和 JdbcUserDetailsManager 均實現介面 UserDetailsManager ,本質就是對於 UserDetails 的 CRUD 。我們先介紹 UserDetails ,然後再分別介紹基於內存和資料庫的實現。
UserDetails是用戶信息的抽象介面:
獲取用戶名。
獲取密碼。
獲取許可權,可以簡單理解為角色名稱(字元串),用於實現介面基於角色的授權訪問,詳情見後文。
獲取用戶是否可用,或用戶/密碼是否過期或鎖定。
Spring Security 提供了一個 UserDetails 的實現類 User ,用於用戶信息的實例表示。另外, User 提供 Builder 模式的對象構建方式。
設置用戶名稱。
設置密碼,Spring Security 不建議使用明文字元串存儲密碼,密碼格式:
其中,id 為加密演算法標識,encodedPassword 為密碼加密後的字元串。這里以加密演算法 bcrypt 為例,詳細內容可參考 Password Storage 。
設置角色,支持多個。
UserDetails實例創建完成之後,就可以使用 UserDetailsManager 的具體實現進行存儲和讀取。
InMemoryUserDetailsManager是 Spring Security 為我們提供的基於內存實現的 UserDetailsManager 。
使用 @Bean 將 InMemoryUserDetailsManager 實例注入 Spring Security。
創建 InMemoryUserDetailsManager 實例之後,並不是必須立即調用 createUser 添加用戶信息,也可以在業務系統的其它地方獲取已注入的 InMemoryUserDetailsManager 動態存儲 UserDetails 實例。
編譯啟動應用,使用我們自己創建的用戶名和密碼(userA/123456)訪問介面:
基於內存介質自定義的用戶名和密碼已生效,介面正常響應。
JdbcUserDetailsManager是 Spring Security 為我們提供的基於資料庫實現的 UserDetailsManager ,相較於 InMemoryUserDetailsManager 使用略復雜,需要我們創建數據表,並准備好資料庫連接需要的數據源(DataSource), JdbcUserDetailsManager 實例的創建依賴於數據源。
JdbcUserDetailsManager可以與業務系統共用一個資料庫數據源實例,本文不討論數據源的相關配置。
以 Mysql 為例,創建數據表語句:
其他資料庫語句可參考 User Schema 。
JdbcUserDetailsManager實例的創建與注入,除
之外,整體流程與 InMemoryUserDetailsManager 類似,不再贅述。
在業務系統中獲取已注入的 JdbcUserDetailsManager 實例,可以動態存儲 UserDetails 實例。
編譯啟動應用,使用我們自己創建的用戶名和密碼(userA/123456)訪問介面:
基於資料庫介質自定義的用戶名和密碼已生效,介面正常響應。
Spring Security 可以提供基於角色的許可權控制:
假設,存在兩個角色 USER(普通用戶) 和 ADMIN(管理員),
角色 USER 可以訪問介面 /hello/name,
角色 ADMIN 可以訪問介面 /hello/world,
所有用戶認證後可以訪問介面 /。
我們需要按上述需求重新設置 HttpSecurity :
設置角色 USER 可以訪問介面 /hello/name。
設置角色 ADMIN 可以訪問介面 /hello/world。
設置其他介面認證後即可訪問。
mvcMatchers 支持使用通配符。
創建屬於角色 USER 和 ADMIN 的用戶:
用戶名:userA,密碼:123456,角色:USER
用戶名:userB,密碼:abcdef,角色:ADMIN
對於用戶 userA :
使用用戶 userA 的用戶名和密碼訪問介面 /:
認證通過,可正常訪問。
使用用戶 userA 的用戶名和密碼訪問介面 /hello/name:
認證通過,鑒權通過,可正常訪問。
使用用戶 userA 的用戶名和密碼訪問介面 /hello/world:
認證通過,用戶 userA 不屬於角色 ADMIN,禁止訪問。
使用用戶 userA 的用戶名和密碼訪問介面 /:
認證通過,可正常訪問。
對於用戶 userB :
使用用戶 userB 的用戶名和密碼訪問介面 /:
認證通過,可正常訪問。
使用用戶 userB 的用戶名和密碼訪問介面 /hello/world:
認證通過,鑒權通過,可正常訪問。
使用用戶 userB 的用戶名和密碼訪問介面 /hello/name:
認證通過,用戶 userB 不屬於角色 USER,禁止訪問。
這里可能會有一點奇怪,一般情況下我們會認為 管理員 應該擁有 普通用戶 的全部許可權,即普通用戶 可以訪問介面 /hello/name,那麼 管理員 應該也是可以訪問介面 /hello/name 的。如何實現呢?
方式一,設置用戶 userB 同時擁有角色 USER 和 ADMIN;
這種方式有點不夠「優雅」。
方式二,設置角色 ADMIN 包含 USER;
Spring Security 有一個 Hierarchical Roles 的特性,可以支持角色之間的 包含 操作。
使用這個特性要特別注意兩個地方:
前文使用的是 HttpSecurity.authorizeHttpRequests 方法,此處需要變更為 HttpSecurity.authorizeRequests 方法。
使用 RoleHierarchy 以 Bean 的方式定義角色之間的 層級關系 ;其中,「ROLE_」 是 Spring Security 要求的固定前綴。
編譯啟動應用,使用用戶 userB 的用戶名和密碼訪問介面 /hello/name:
認證通過,鑒權通過,可正常訪問。
如果開啟 Spring Security 的 debug 日誌級別,訪問介面時可以看到如下的日誌輸出:
可以看出,Spring Security 可以從角色 ADMIN 推導出用戶實際擁有 USER 和 ADMIN 兩個角色。
Hierarchical Roles 文檔中的示例有明顯錯誤:
介面 RoleHierarchy 中並不存在方法 setHierarchy 。前文所述 authorizeRequests 和 RoleHierarchy 結合使用的方法是結合網路搜索和自身實踐得出的,僅供參考。
另外, authorizeHttpRequests 和 RoleHierarchy 結合是沒有效果的, authorizeRequests 和 authorizeHttpRequests 兩者之間的區別可以分別參考 Authorize HttpServletRequests with AuthorizationFilter 和 Authorize HttpServletRequest with FilterSecurityInterceptor 。
鑒權的前提需要認證通過;認證不通過的狀態碼為401,鑒權不通過的狀態碼為403,兩者是不同的。
Spring Security 異常主要分為兩種:認證失敗異常和鑒權失敗異常,發生異常時會分別使用相應的默認異常處理器進行處理,即:認證失敗異常處理器和鑒權失敗異常處理器。
使用的認證或鑒權實現機制不同,可能使用的默認異常處理器也不相同。
Spring Security 認證失敗異常處理器:
如前文所述,認證失敗時,Spring Security 使用默認的認證失敗處理器實現返回:
如果想要自定義返回內容,則可以通過自定義認證失敗處理器實現:
authenticationEntryPoint()會創建返回一個自定義的 AuthenticationEntryPoint 實例;其中,使用 HttpServletResponse.getWriter().print() 寫入我們想要返回的內容:401。
httpBasic().authenticationEntryPoint(authenticationEntryPoint())使用我們自定義的 AuthenticationEntryPoint 替換 HttpBasic 默認的 BasicAuthenticationEntryPoint 。
編譯啟動應用,使用不正確的用戶名和密碼訪問介面 /:
認證不通過,使用我們自定義的內容 401 返回。
Spring Security 鑒權失敗異常處理器:
如前文所述,認證失敗時,Spring Security 使用默認的認證失敗處理器實現返回:
如果想要自定義返回內容,則可以通過自定義鑒權失敗處理器實現:
自定義鑒權失敗處理器與認證失敗處理器過程類似,不再贅述。
編譯啟動應用,使用用戶 userA 的用戶名和密碼訪問介面 /hello/world:
鑒權不通過,使用我們自定義的內容 403 返回。
exceptionHandling()也是有一個 authenticationEntryPoint() 方法的;對於 HttpBasic 而言,使用 exceptionHandling().authenticationEntryPoint() 設置自定義認證失敗處理器是不生效的,具體原因需要大家自行研究。
前文介紹兩種認證方式: FormLogin 和 HttpBasic ,Spring Security 還提供其他若干種認證方式,詳情可參考 Authentication Mechanisms 。
如果我們想實現自己的認證方式,也是比較簡單的。Spring Security 本質就是 過濾器 ,我們可以實現自己的認證過濾器,然後加入到 Spring Security 中即可。
認證過濾器核心實現流程:
除去拋出異常的情況外, filterChain.doFilter(servletRequest, servletResponse); 是必須保證被執行的。
理解認證過濾器涉及的概念會比較多,詳情參考 Servlet Authentication Architecture 。
認證過濾器創建完成之後,就可以加入到 Spring Security 中:
Spring Security 根據我們配置的不同,會為我們自動按照一定的次序組裝一條 過濾器鏈 ,通過這條鏈上的若干過濾器完成認證鑒權的。我們需要把自定義的認證過濾器加到這個鏈的 合適位置 ,這是選取的位置是在 ExceptionTranslationFilter 的前面。
過濾器鏈的順序可以參考 Security Filters 。
ExceptionTranslationFilter 的作用可以參考 Handling Security Exceptions 。
使用自定義認證過濾器時,自定義認證失敗異常處理器和鑒權失敗異常處理器的設置方法。
編譯啟動應用,我們會發現可以在不填入任何認證信息的情況下直接訪問介面 / 和 /hello/name,因為模擬用戶已認證且角色為 USER;訪問介面 /hello/world 時會出現提示 403。
Spring Security 自身包含的內容很多,官方文檔也不能很好的講述清楚每個功能特性的使用方法,很多時候需要我們自己根據文檔、示例、源碼以及他人的分享,盡可能多的實踐,逐步加深理解。
3. Spring的通知有哪幾種類型
Term 術語 Definition 定義 Concern A particular issue (關注特定問題) 感興趣應用的特定問題、概念、范圍。例如,事務管理、持久化、日誌、安全等。 Crosscutting Concern (橫切關注點) 在關注點實現中貫穿了很多類,這在面向對象(OOP)中通常很難實現和維護。 Aspect(切面) 模塊化的橫切關注點,通過代碼的聚合和隔離實現。 Join Point(連接點) 在程序或者類執行時的一個點。在Spring的AOP實現中,連接點總是一個方法調用。其他的例子包括訪問欄位(包括實例中欄位的讀寫),變數和異常處理。 Advice(通知) 特定連接點所採取的動作。Spring有幾種不同類型的通知,包括around、before、throws和after returning。在這幾種類型的通知中,around是最強大的,在方法調用的前後都有執行一些操作的機會。之前用到的TraceInterceptor就是around類型的通知,它實現了AOP聯盟的MethodInterceptor介面。通過實現下面的Spring介面可以使用其他類型的通知: �0�1MethodBeforeAdvice �0�1ThrowsAdvice �0�1AfterReturningAdvicePointcut(切入點) 連接點的集合,這些連接點確認何時一個通知將會觸發。切入點通常使用正則表達式或者是通配符語法。 Introction (引入) 添加欄位或者方法到一個通知類。Spring允許你在任何通知對象上引入新的介面。例如,你可以使用引入以便讓任意對象實現IsModified介面,來簡化緩存。 Weaving(組織) 裝配切面來創建一個被通知對象。可以在編譯期間完成(像AspectJ做的那樣)也可以在運行時完成。本章後面的組織策略部分詳細討論不同的組織策略(也就是實現AOP)。 Interceptor (攔截器) 一種AOP實現策略,對與特點的連接點,可能存在一個攔截器鏈。 AOP Proxy(AOP代理) AOP框架創建的對象,包括通知。在Spring中,一個AOP代理可能是JDK動態代理或者是CGLIB代理。 Target Object (目標對象) 包含連接點的對象。在使用攔截的框架中,它是在攔截器鏈末端的對象實例。也叫做被通知對象或者被代理對象。 切面。 用個比喻會更形象一些。可以把切面理解成一把菜刀,而其他方法就好像是黏糕,如果你要把所有的黏糕都在某個 位置切斷,那麼就用這個菜刀就行了。這個菜刀,就是我們需要的切面類。 例如execution(* com.xyz.someapp.service.*.*(..)) 尾部(After),還是首尾(Around)等等。 這些「插入」的代碼都是程序運行的時候動態插入的,所以看起來很神奇
4. java程序是不是應用了介面,就基本上可以實現spring了
當然不是了,spring是Java EE里最成功的一個框架,要實現spring需要把spring的jar包以及其他相關的jar包加到項目路徑里,而且還要有很多配置。知識spring里用的介面多一點,並不是用了介面就實現了spring,介面是java se里的一個基本特性
5. Spring核心介面是
1.BeanFactory最基礎最核心的介面
重要的實現類有:
XmlBeanFactory,以及ApplicationContext介面下的類
2.Resource介面,可以通用地訪問文件資源
1)ClassPathResource:讀取得形式為"classpath:ApplicationContext.xml"
2)FileStstemResource:讀取得形式為"file:c:\spring\src\ApplicationContext.xml"
3)ServletContextResource:讀取得形式為"WEB-INF\ApplicationContext.xml"
//ResourceLoader類用於載入Resource
3.FactoryBean工廠bean
它本身在bean factory中定義,同時又是用於創建目標bean的工廠
spring有以下實現:
1)JndiObjectFactoryBean:通過JNDI查找獲取對象
2)LocalSessionFactoryBean:用於在本地裝配Hibernate SessionFactory
3)LocalSessionFactoryBean:用於在本地裝配JDO PersistenceManagerFactory
4)ProxyFactoryBean:用於獲取AOP的代理
5)TransactionProxyFactoryBean:用於為對象創建事務代理,用於實現簡捷易用的聲明性事務代理
6)RmiProxyFactoryBean:為通過RMI訪問的遠程對象創建一個代理
4.ApplicationContext加強了BeanFactory的功能,支持以下功能
1)國際化支持
2)資源訪問
3)事件監聽機制
它的重要的實現類有:
,ClassPathXmlApplication,XmlWebApplicationContext
5.Bean生命周期的回調介面
BeanFactoryPostProcessor,InitializingBean,DisposableBean,BeanFactoryAware,ApplicationContextAware,ResourceLoader,BeanPostProcessor
6.MessageSource介面
它的實現類有ResourceBundleMessageSource,.後者在屬性文件修改後會重新載入
如果喜歡共同談論, 加msn :[email protected]
6. 為什麼ibatis在和spring一起使用的時候,可以不用實現類就用介面就可以查出來
因為sql語句都是寫在ibatis的配置文件中了,<select id="方法名" 這個是ibatis的配置中sql查詢語言的節點,如果你要寫查詢語句,就配置這個節點,把sql語句寫在這個節點裡面,id屬性不是方法名,是這個節點對應的一個id值,ibatis在調用查詢方法的時候,在實現中中需要把這個id值作為參數告訴ibatis,從而ibatis知道去調用那條語句,
7. spring 為什麼要先寫介面,再寫實現類
首先你要理解OOP的思想,是面向介面編程.
什麼叫面向介面編程呢?
假如你買了一個多媒體設備,它給了你一個遙控,你想要知道的只是按什麼按鈕,它會播放什麼
而遙控裡面是怎樣運行,還有屏幕裡面怎麼工作,你想知道嗎?
你完全不會去想了解.
那如果多媒體設備需要更新,比如優化內部運行效率,
但是優化完了,遙控的按鈕不變,設備的所有操作方式都不變,按這個按鈕還是顯示相同的東西
那內部怎麼變化你完全不需要在意.
這就是面向介面編程.
無論類的內部怎麼實現,它對外的介面不變,那它的使用方式就不會變
假設Main類要使用D類的一個draw的方法,
方法名叫 draw():void
不管draw裡面是怎樣的,Main類里就是這樣用,
那麼你就從這個介面出發,裡面怎麼實現是D類的事了,Main類只關心怎麼用而已.
其他類要使用它,還是相同
這就大大減少了維護的成本.
因為如果D類出問題,Main類是完全不用改變的.
從上觀察,公開的介面越多,維護成本就越大.
維護就越麻煩.所以我們先寫介面,定死了公開的介面,
那維護就很方便,出錯也只是一個類的事,而不用同時修改多個協同類
8. spring,applicationContext.xml配置,載入一直報錯啊,類用了介面,不用介面同樣配置就可以。求救啊
最好把applicationContext.xml關鍵代碼貼出來。從錯誤信息來看,應該是tService這個屬性沒找到,看下tService對應的bean有沒有在xml中作配置。
9. 為什麼spring注入介面正確而注入介面的實現
原因所在:出現如果直接注入實現類會出現沒有對應的bean,因為我們通過實現類來繼承的介面,然而,必須使用jdk提供的動態代理放法,而不使用介面直接對實現類進行注入,則為cglib的注入,而不能既繼承介面又使用實現類來注入的方式,這樣兩種代理類都是沒有辦法使用的。
解決方案:1.直接通過jdk去生成動態代理類,(原理要求必須實現介面)
2.通過cglib去實現介面,直接使用代理類,而不能實現介面。
10. 怎麼把spring boot 在沒有驗證的情況下也訪問介面
呵呵,聯想的有些電腦,其實很多筆記本都是那樣的,它的開機啟動也就是boot是按F12的,有些是按ESC,有些是F2,聯想的大多是F12