javascript
使用PostgreSQL使用Spring Boot和JPA构建基本应用
“我喜歡編寫身份驗(yàn)證和授權(quán)代碼。” ?從來沒有Java開發(fā)人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進(jìn)行托管身份驗(yàn)證,授權(quán)和多因素身份驗(yàn)證。
每個(gè)不平凡的應(yīng)用程序都需要一種保存和更新數(shù)據(jù)的方法:可通過HTTP訪問的資源服務(wù)器。 通常,必須保護(hù)此數(shù)據(jù)。 Java是一門偉大的語言,在專業(yè),企業(yè)開發(fā)方面已有數(shù)十年的歷史,對(duì)于任何應(yīng)用程序的服務(wù)器堆棧都是絕佳的選擇。 在Java生態(tài)系統(tǒng)內(nèi),Spring使為數(shù)據(jù)構(gòu)建安全的資源服務(wù)器變得簡(jiǎn)單。 與Okta結(jié)合使用時(shí),您可以使用Spring Security將經(jīng)過專業(yè)維護(hù)的OAuth和JWT技術(shù)輕松集成到Spring Boot中。
在本文中,您將使用Spring Boot和Spring Data JPA構(gòu)建資源服務(wù)器。 最重要的是,您將使用OAuth 2.0實(shí)現(xiàn)基于組的身份驗(yàn)證和授權(quán)層。 如果這聽起來很復(fù)雜–不用擔(dān)心! 不是。
在深入探討之前,讓我們介紹一下背景知識(shí):
資源服務(wù)器是服務(wù)器功能和數(shù)據(jù)的編程訪問點(diǎn)(與API服務(wù)器和/或REST服務(wù)器基本相同)。
JPA是Java Persistence API,它是使用Java管理關(guān)系數(shù)據(jù)庫的規(guī)范。 它描述了Java類和關(guān)系數(shù)據(jù)庫之間的抽象層。
Spring Data JPA是JPA提供程序(例如Hibernate)的包裝。 正如您將看到的,它使持久化Java類就像添加一些注釋和創(chuàng)??建簡(jiǎn)單的存儲(chǔ)庫接口一樣簡(jiǎn)單。 無需實(shí)際編寫持久性或檢索方法! 另一個(gè)很大的好處是您可以透明地更改基礎(chǔ)數(shù)據(jù)庫實(shí)現(xiàn),而不必更改任何代碼。 例如,在本教程中,您將使用Postgres,但稍后,如果您決定使用MySQL,您要做的就是更改一些依賴項(xiàng)。
安裝PostgreSQL以實(shí)現(xiàn)JPA持久性
您需要為此教程安裝PostgreSQL。 如果尚未安裝,請(qǐng)轉(zhuǎn)到其下載頁面并進(jìn)行安裝。
接下來需要做的是為項(xiàng)目創(chuàng)建一個(gè)Postgres用戶和數(shù)據(jù)庫。 為此,您可以使用Postgres CLI。 您應(yīng)該能夠運(yùn)行以下命令: psql -V并得到如下響應(yīng):
psql (PostgreSQL) 11.12為您的JPA實(shí)體創(chuàng)建PostgreSQL數(shù)據(jù)庫
在使用數(shù)據(jù)庫之前,您需要做一些事情。 你需要:
本教程使用jpatutorial作為用戶名,并使用springbootjpa作為數(shù)據(jù)庫名。 如果愿意,可以隨意更改這些值,但是您必須記住在整個(gè)教程中都使用自定義值。
從終端輸入psql進(jìn)入Postgres shell。 然后輸入以下命令。
創(chuàng)建一個(gè)用戶
create user jpatutorial;外殼程序應(yīng)響應(yīng): CREATE ROLE
不要忘記分號(hào)! 我永遠(yuǎn)也不會(huì)這樣做。 我絕對(duì)不是憑經(jīng)驗(yàn)說話。 但是,如果你沒有在分號(hào)鍵入psql不處理的命令,你可以在沮喪迷茫失去20-30分鐘,不知道發(fā)生了什么事,直到你進(jìn)入一個(gè)分號(hào),在這一點(diǎn)上,試圖處理所有的命令。
給用戶密碼
alter user jpatutorial with encrypted password '<your really secure password>';外殼程序應(yīng)使用以下命令響應(yīng): ALTER ROLE 。
創(chuàng)建數(shù)據(jù)庫
create database springbootjpa;外殼程序應(yīng)使用以下命令響應(yīng): CREATE DATABASE 。
授予特權(quán)
grant all privileges on database springbootjpa to jpatutorial;外殼應(yīng)以GRANT響應(yīng)。
最后,如果需要,鍵入\q退出外殼。
如果您想了解更多有關(guān)psql ,可以看看Postgres的docs 。
構(gòu)建一個(gè)Spring Boot資源服務(wù)器
從GitHub倉庫克隆啟動(dòng)項(xiàng)目,并檢出start分支:
git clone -b start https://github.com/oktadeveloper/okta-spring-boot-jpa-example.git入門項(xiàng)目是一個(gè)全新的Spring Boot項(xiàng)目,僅具有一些Postgres特定的配置。 如果查看build.gradle文件,將看到PostgreSQL JPA連接器依賴性。 您還會(huì)注意到文件src/main/resources/hibernate.properties其唯一目的是擺脫對(duì)我們而言并不重要的煩人的警告/錯(cuò)誤。 src/main/resources/application.yml文件還為您預(yù)先填充了一些屬性。
繼續(xù)并打開application.yml文件,并填寫您為數(shù)據(jù)庫用戶創(chuàng)建的密碼。 您還應(yīng)該更新用戶名,數(shù)據(jù)庫名稱和端口(如果它們不同)。
spring: jpa: hibernate: ddl-auto: create database-platform: org.hibernate.dialect.PostgreSQLDialect datasource: url: "jdbc:postgresql://localhost:5432/springbootjpa" username: jpatutorial password: < your password >ddl-auto屬性指定了加載時(shí)休眠的行為。 選項(xiàng)包括:
- validate: 驗(yàn)證架構(gòu),但不做任何更改
- 更新: 更新架構(gòu)
- create: 創(chuàng)建模式,銷毀任何先前的數(shù)據(jù)
- create-drop: 類似于create,但是在會(huì)話關(guān)閉時(shí)也會(huì)刪除架構(gòu)(用于測(cè)試)
您正在使用create 。 每次運(yùn)行該程序時(shí),都會(huì)從新表和數(shù)據(jù)開始創(chuàng)建一個(gè)新的數(shù)據(jù)庫。
database-platform實(shí)際上是不必要的。 Spring Data / Hibernate可以自動(dòng)檢測(cè)平臺(tái)。 但是,如果沒有此屬性,那么如果您在未啟動(dòng)Postgres服務(wù)器的情況下運(yùn)行應(yīng)用程序,則會(huì)得到一個(gè)非常無益的錯(cuò)誤,即未添加此config屬性而不是被告知要啟動(dòng)服務(wù)器。 發(fā)生這種情況是因?yàn)镠ibernate無法自動(dòng)檢測(cè)數(shù)據(jù)庫平臺(tái),因此在抱怨實(shí)際上沒有正在運(yùn)行的服務(wù)器之前先抱怨一下。
使用./gradlew bootRun運(yùn)行應(yīng)用程序。 您應(yīng)該會(huì)看到以下內(nèi)容:
2018-11-21 09:27:50.233 INFO 31888 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource] 2018-11-21 09:27:50.302 INFO 31888 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2018-11-21 09:27:50.308 INFO 31888 --- [ main] c.o.s.SpringBootJpaApplication : Started SpringBootJpaApplication in 21.361 seconds (JVM running for 21.848) <=========----> 75% EXECUTING [4m 26s] > :bootRun但是,它并沒有做太多事情。 沒有域模型,資源存儲(chǔ)庫或控制器類。
使用Spring Data和JPA添加域類
域或模型是您將存儲(chǔ)的數(shù)據(jù)的程序表示形式。 Spring Data和JPA的神奇之處在于,Spring可以采用Java類并將其轉(zhuǎn)換為數(shù)據(jù)庫表。 它甚至?xí)詣?dòng)生成必要的加載和保存方法。 最好的部分是(或多或少)這與數(shù)據(jù)庫無關(guān)。
您在本教程中使用的是PostgreSQL,并且可以通過更改build.gradle文件中的依賴項(xiàng)輕松地將其切換到MySQL。 當(dāng)然,還要?jiǎng)?chuàng)建一個(gè)MySQL數(shù)據(jù)庫并更新application.yml文件中的必要屬性。 這對(duì)于測(cè)試,開發(fā)和長(zhǎng)期維護(hù)非常有用。
繼續(xù)閱讀以學(xué)習(xí)如何開發(fā)一個(gè)簡(jiǎn)單的服務(wù)器來存儲(chǔ)皮劃艇類型。
在com.okta.springbootjpa程序包中創(chuàng)建一個(gè)名為Kayak.java的Java文件。 您的皮劃艇模型將具有名稱,所有者,值和品牌/模型。
package com.okta.springbootjpa;import lombok.Data;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;@Entity // This tells Hibernate to make a table out of this class @Data // Lombok: adds getters and setters public class Kayak {public Kayak(String name, String owner, Number value, String makeModel) {this.name = name;this.owner = owner;this.value = value;this.makeModel = makeModel;}@Id@GeneratedValue(strategy=GenerationType.AUTO)private Integer id;private final String name;private String owner;private Number value;private String makeModel; }該項(xiàng)目使用Lombok來避免必須編寫大量的儀式獲取器,設(shè)置器和諸如此類的東西。 您可以查看他們的文檔 ,或更具體地說是您正在使用的@Data注釋 。
@Entity注釋告訴Spring此類是模型類,應(yīng)轉(zhuǎn)換為數(shù)據(jù)庫表。
大多數(shù)屬性可以自動(dòng)映射。 但是, id屬性用幾個(gè)注釋修飾,因?yàn)槲覀冃枰嬖VJPA這是ID字段,并且應(yīng)該自動(dòng)生成它。
使用Spring Data JPA實(shí)現(xiàn)CRUD存儲(chǔ)庫
定義了域類后,Spring知道足以構(gòu)建數(shù)據(jù)庫表,但是它沒有定義任何控制器方法。 沒有數(shù)據(jù)的輸出或輸入。 Spring使添加資源服務(wù)器變得微不足道。 實(shí)際上,它是如此瑣碎,您可能不會(huì)相信。
在包c(diǎn)om.okta.springbootjpa ,創(chuàng)建一個(gè)名為KayakRepository.java的接口。
package com.okta.springbootjpa;import org.springframework.data.repository.CrudRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource;@RepositoryRestResource public interface KayakRepository extends CrudRepository<Kayak, Integer> { } 而已!
現(xiàn)在,您可以從資源服務(wù)器創(chuàng)建,讀取,更新和刪除皮劃艇。 在短短的幾秒鐘內(nèi),您將精確地完成此操作,但在此之前,請(qǐng)進(jìn)行其他更改。
將以下init()方法添加到SpringBootJpaApplication類中:
應(yīng)用程序啟動(dòng)時(shí)將運(yùn)行此方法。 它將一些樣本數(shù)據(jù)加載到資源服務(wù)器中,只是為了讓您在下一節(jié)中有所了解。
測(cè)試您的Spring Boot資源服務(wù)器
HTTPie是一個(gè)很棒的命令行實(shí)用工具,它使對(duì)資源服務(wù)器的請(qǐng)求運(yùn)行變得容易。 如果未安裝HTTPie,請(qǐng)使用brew install httpie進(jìn)行brew install httpie 。 或前往他們的網(wǎng)站并實(shí)現(xiàn)它。 或者只是跟隨。
確保您的Spring Boot應(yīng)用正在運(yùn)行。 如果不是,請(qǐng)使用./gradlew bootRun啟動(dòng)它。
針對(duì)您的資源服務(wù)器運(yùn)行GET請(qǐng)求: http :8080/kayaks ,這是http GET http://localhost:8080/kayaks簡(jiǎn)寫。
您會(huì)看到以下內(nèi)容:
HTTP/1.1 200 Content-Type: application/hal+json;charset=UTF-8 Date: Wed, 21 Nov 2018 20:39:11 GMT Transfer-Encoding: chunked{"_embedded": {"kayaks": [{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/1"},"self": {"href": "http://localhost:8080/kayaks/1"}},"makeModel": "NDK","name": "sea","owner": "Andrew","value": 300.12},{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/2"},"self": {"href": "http://localhost:8080/kayaks/2"}},"makeModel": "Piranha","name": "creek","owner": "Andrew","value": 100.75},{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/3"},"self": {"href": "http://localhost:8080/kayaks/3"}},"makeModel": "Necky","name": "loaner","owner": "Andrew","value": 75}]},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}} }此輸出使您對(duì)Spring Boot資源返回的數(shù)據(jù)格式有了一個(gè)非常扎實(shí)的想法。 您也可以使用POST添加新的皮劃艇。
命令:
http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H"回復(fù):
HTTP/1.1 201 Content-Type: application/json;charset=UTF-8 Date: Wed, 21 Nov 2018 20:42:14 GMT Location: http://localhost:8080/kayaks/4 Transfer-Encoding: chunked{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/4"},"self": {"href": "http://localhost:8080/kayaks/4"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500 }如果您再次列出皮劃艇( http :8080/kayaks ),則會(huì)在列出的項(xiàng)目中看到新的皮劃艇。
HTTP/1.1 200 Content-Type: application/hal+json;charset=UTF-8 Date: Wed, 21 Nov 2018 20:44:22 GMT Transfer-Encoding: chunked{"_embedded": {"kayaks": [...{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/4"},"self": {"href": "http://localhost:8080/kayaks/4"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500}]},... }您也可以刪除皮劃艇。 運(yùn)行以下命令: http DELETE :8080/kayaks/4這將刪除ID = 4的皮劃艇或我們剛剛創(chuàng)建的皮劃艇。 第三次獲取皮艇列表,您會(huì)發(fā)現(xiàn)它已經(jīng)消失了。
使用Spring Boot,只需很少的代碼,就可以創(chuàng)建功能全面的資源服務(wù)器。 此數(shù)據(jù)將持久保存到您的Postgres數(shù)據(jù)庫中。
您可以使用Postgres命令外殼來驗(yàn)證這一點(diǎn)。 在終端上,鍵入psql進(jìn)入shell,然后鍵入以下命令。
連接到數(shù)據(jù)庫:
\connect springbootjpapsql (9.6.2, server 9.6.6) You are now connected to database "springbootjpa" as user "cantgetnosleep".顯示表內(nèi)容:
SELECT * FROM kayak;id | make_model | name | owner | value ----+------------+--------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------1 | NDK | sea | Andrew | \xaced0005737200106a6176612e6c616e67...8704072c1eb851eb8522 | Piranha | creek | Andrew | \xaced0005737200106a6176612e6c616e672e...0787040593000000000003 | Necky | loaner | Andrew | \xaced00057372000e6a6176612e6c616e67...7870000000000000004b5 | P&H | sea2 | Andrew | \xaced0005737200116a6176612e6...08b0200007870000001f4 (4 rows)需要注意的幾件事。 首先,請(qǐng)注意, 值被存儲(chǔ)為二進(jìn)制對(duì)象,因?yàn)樗欢x為Number類型而不是原始類型(double,float或int)。 其次,請(qǐng)記住,由于ddl-auto: create application.yml文件中的ddl-auto: create行,在application.yml每次啟動(dòng)時(shí)都將擦除此數(shù)據(jù)并重新創(chuàng)建整個(gè)表。
設(shè)置身份驗(yàn)證
Okta是軟件即服務(wù)身份,身份驗(yàn)證和授權(quán)提供者。 雖然我確實(shí)從事過將所有項(xiàng)目外包給SaaS提供商的項(xiàng)目,但所產(chǎn)生的問題超出了其承諾解決的問題,但身份驗(yàn)證和授權(quán)是使這種模型完全有意義的地方。 在線安全很難。 發(fā)現(xiàn)漏洞,必須快速更新服務(wù)器。 標(biāo)準(zhǔn)變更和代碼需要修改。 所有這些更改都有可能創(chuàng)建新的漏洞。 讓Okta處理安全性意味著您可以擔(dān)心使您的應(yīng)用程序與眾不同的事情。
為了向您展示設(shè)置的簡(jiǎn)便性,您將集成Okta OAuth并將基于令牌的身份驗(yàn)證添加到資源服務(wù)器。 如果尚未注冊(cè),請(qǐng)?jiān)L問developer.okta.com并注冊(cè)一個(gè)免費(fèi)帳戶。 擁有帳戶后,通過單擊“ 應(yīng)用程序”頂部菜單項(xiàng),然后單擊“ 添加應(yīng)用程序”按鈕,打開開發(fā)人員儀表板并創(chuàng)建OpenID Connect(OIDC)應(yīng)用程序 。
選擇單頁應(yīng)用程序 。
默認(rèn)應(yīng)用程序設(shè)置很好,除了您需要添加登錄重定向URI : a 。 您將在稍后使用它來檢索測(cè)試令牌。
另外,請(qǐng)注意您的客戶ID ,稍后您將需要它。
配置您的Spring Boot資源服務(wù)器以進(jìn)行令牌認(rèn)證
Okta使添加令牌身份驗(yàn)證到Spring Boot非常容易。 他們有一個(gè)名為Okta Spring Boot Starter的項(xiàng)目( 請(qǐng)查看GitHub項(xiàng)目 ),將整個(gè)過程簡(jiǎn)化為幾個(gè)簡(jiǎn)單的步驟。
在您的build.gradle文件中添加幾個(gè)依賴build.gradle 。
compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.0.RELEASE') compile('com.okta.spring:okta-spring-boot-starter:0.6.1')將以下內(nèi)容添加到build.gradle文件的底部(這解決了logback日志記錄依賴性沖突)。
configurations.all { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' exclude group: 'org.springframework.boot', module: 'logback-classic' }接下來,你需要一些配置添加到您的application.yml文件,替換{yourClientId}從你1563 OIDC應(yīng)用程序和客戶端ID {yourOktaDomain}與1563網(wǎng)址。 像https://dev-123456.oktapreview.com東西。
okta: oauth2: issuer: https://{yourOktaDomain}/oauth2/default client-id: {yourClientId} scopes: openid profile email最后,您需要將@EnableResourceServer批注添加到SpringBootVueApplication類。
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;@EnableResourceServer // <- add me @SpringBootApplication public class SpringBootJpaApplication { public static void main(String[] args) { SpringApplication.run(SpringBootJpaApplication.class, args); }... }測(cè)試受保護(hù)的Spring Boot服務(wù)器
停止您的Spring Boot服務(wù)器并使用以下./gradlew bootRun重新啟動(dòng)它: ./gradlew bootRun
從命令行運(yùn)行一個(gè)簡(jiǎn)單的GET請(qǐng)求。
http :8080/kayaks您會(huì)得到未經(jīng)授權(quán)的401 /。
HTTP/1.1 401 Cache-Control: no-store Content-Type: application/json;charset=UTF-8{"error": "unauthorized","error_description": "Full authentication is required to access this resource" }生成訪問令牌
要立即訪問服務(wù)器,您需要一個(gè)有效的訪問令牌。 您可以使用OpenID Connect調(diào)試器來幫助您完成此任務(wù)。 在另一個(gè)窗口中,打開oidcdebugger.com 。
授權(quán)URI : https://{yourOktaUrl}/oauth2/default/v1/authorize ,其中{yourOktaUrl}替換為您的實(shí)際Okta預(yù)覽URL。
重定向URI :不變。 這是您在上面的OIDC應(yīng)用程序中添加的值。
客戶ID :來自您剛創(chuàng)建的OIDC應(yīng)用程序。
范圍 : openid profile email 。
狀態(tài) :您要通過OAuth重定向過程傳遞的任何值。 我將其設(shè)置為{} 。
Nonce :可以一個(gè)人呆著。 Nonce表示“編號(hào)已使用一次”,是一種簡(jiǎn)單的安全措施,用于防止同一請(qǐng)求被多次使用。
響應(yīng)類型 : token 。
響應(yīng)方式 : form_post 。
點(diǎn)擊發(fā)送請(qǐng)求 。 如果您尚未登錄developer.okta.com,則需要登錄。如果(可能的話)已經(jīng)登錄,則將為您的登錄身份生成令牌。
使用訪問令牌
您可以通過在Bearer類型的Authorization請(qǐng)求標(biāo)頭中包含令牌來使用令牌。
Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJfNVJr...通過HTTPie發(fā)出請(qǐng)求:
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1...'添加基于組的授權(quán)
到目前為止,授權(quán)方案還算是二進(jìn)制的。 該請(qǐng)求是否帶有有效令牌。 現(xiàn)在,您將添加基于組的身份驗(yàn)證。 請(qǐng)注意,盡管有時(shí)在臭名昭著的網(wǎng)站上可以互換使用,但角色和組卻不是一回事,它們是實(shí)現(xiàn)授權(quán)的不同方法。
角色是用戶可以繼承的權(quán)限集合的集合。 組是一組標(biāo)準(zhǔn)權(quán)限分配給的用戶的集合。 但是,在令牌的范圍以及如何將Spring Security與JPA結(jié)合使用時(shí),實(shí)現(xiàn)是完全相同的。 它們都以字符串“ authority”的形式從OAuth OIDC應(yīng)用程序傳遞給Spring,因此目前它們基本上可以互換。 不同之處在于受保護(hù)的內(nèi)容及其定義方式。
要在Okta中使用基于組的授權(quán),您需要在訪問令牌中添加一個(gè)“組”聲明。 創(chuàng)建一個(gè)Admin組(“ 用戶” >“ 組” >“ 添加組” )并將您的用戶添加到其中。 您可以使用注冊(cè)時(shí)使用的帳戶,也可以創(chuàng)建一個(gè)新用戶(“ 用戶” >“ 添加人” )。 導(dǎo)航到“ API” >“ 授權(quán)服務(wù)器” ,單擊“ 授權(quán)服務(wù)器”選項(xiàng)卡,然后編輯默認(rèn)選項(xiàng)卡。 點(diǎn)擊索賠標(biāo)簽,然后添加索賠 。 將其命名為“組”,并將其包含在訪問令牌中。 將值類型設(shè)置為“ Groups”,并將過濾器設(shè)置為.*的正則表達(dá)式。
使用OIDC調(diào)試器創(chuàng)建一個(gè)新的訪問令牌。 通過轉(zhuǎn)到j(luò)sonwebtoken.io并輸入生成的訪問令牌,來查看已解碼的令牌。
有效載荷看起來像這樣:
{"ver": 1,"jti": "AT.Hk8lHezJNw4wxey1czypDiNXJUxIlKmdT16MrnLGp9E","iss": "https://dev-533919.oktapreview.com/oauth2/default","aud": "api://default","iat": 1542862245,"exp": 1542866683,"cid": "0oahpnkb44pcaOIBG0h7","uid": "00ue9mlzk7eW24e8Y0h7","scp": ["email","profile","openid"],"sub": "andrew.hughes@mail.com","groups": ["Everyone","Admin"] }組聲明包含用戶分配到的組。 您用來登錄developer.okta.com網(wǎng)站的用戶也將是“所有人”組和“管理員”組的成員。
為了使Spring Boot和資源服務(wù)器在基于組的授權(quán)下都能正常運(yùn)行,您需要對(duì)代碼進(jìn)行一些更改。
首先,在com.okta.springbootjpa包中添加一個(gè)名為SecurityConfiguration的新Java類。
package com.okta.springbootjpa;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { }需要此配置類來啟用@PreAuthorize批注,該批注將用于基于組成員身份保護(hù)資源服務(wù)器。
接下來,將@PreAuthorize批注添加到KayakRepository ,如下所示:
... import org.springframework.security.access.prepost.PreAuthorize; ...@RepositoryRestResource @PreAuthorize("hasAuthority('Admin')") public interface KayakRepository extends CrudRepository<Kayak, Long> { }最后,在SpringBootJpaApplication , 刪除 ApplicationRunner init(KayakRepository repository)方法(或僅注釋掉@Bean批注)。 如果跳過此步驟,構(gòu)建將失敗,并顯示以下錯(cuò)誤:
AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext@PreAuthorize批注實(shí)際上阻止init()方法以編程方式創(chuàng)建自舉數(shù)據(jù),因?yàn)闆]有用戶登錄。因此,該方法運(yùn)行時(shí)會(huì)引發(fā)錯(cuò)誤。
請(qǐng)注意,您在@PreAuthorize批注中使用hasAuthority() ,而不是 hasRole() 。 區(qū)別在于hasRole()期望組或角色在ALL CAPS中并且具有ROLE_前綴。 這可以被配置,當(dāng)然,但hasAuthority()來沒有這個(gè)包袱,簡(jiǎn)單地檢查任何權(quán)利要求你定義為okta.oauth2.roles-claim在application.yml 。
在您的Spring Boot應(yīng)用程序中測(cè)試管理員用戶
重新啟動(dòng)您的Spring Boot應(yīng)用程序(以./gradlew bootRun )。
嘗試未經(jīng)身份驗(yàn)證的GET請(qǐng)求:v class =“ highlighter-rouge”> http:8080 / kayaks。
HTTP/1.1 401 Cache-Control: no-store Content-Type: application/json;charset=UTF-8{"error": "unauthorized","error_description": "Full authentication is required to access this resource" }使用令牌嘗試一下。
命令:
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf...'回復(fù):
HTTP/1.1 200 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/hal+json;charset=UTF-8{"_embedded": {"kayaks": []},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}} }有效! 我們沒有任何皮劃艇,因?yàn)槲覀儽仨殑h除上面的init()方法,因此_embedded.kayaks數(shù)組為空。
提示:今后,如果您不想復(fù)制并粘貼整個(gè)巨大的令牌字符串,則可以將其存儲(chǔ)到shell變量中,然后像這樣重用它:
TOKEN=eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf... http :8080/kayaks 'Authorization: Bearer $TOKEN'創(chuàng)建一個(gè)非管理員用戶
為了演示基于組的授權(quán),您需要在Okta上創(chuàng)建一個(gè)不是管理員的新用戶。 轉(zhuǎn)到developer.okta.com儀表板。
從頂部菜單中,選擇“ 用戶和人員” 。
單擊添加人按鈕。
給用戶一個(gè)名字 , 姓氏和用戶名 (也將是主要電子郵件 )。 值無關(guān)緊要,并且您將不必檢查電子郵件。 您只需要知道電子郵件地址/用戶名和密碼,即可在一分鐘內(nèi)登錄Okta。
密碼 :將下拉菜單更改為“ 由管理員設(shè)置” 。
為用戶分配密碼。
點(diǎn)擊保存 。
您剛剛創(chuàng)建的用戶不是Admin組的成員,而是默認(rèn)組Everyone的成員。
在Spring Boot應(yīng)用程序中基于測(cè)試組的授權(quán)
注銷您的Okta開發(fā)人員儀表板。
返回OIDC調(diào)試器并生成一個(gè)新令牌。
這次,以新的非管理員用戶身份登錄。 系統(tǒng)會(huì)要求您選擇一個(gè)安全問題,然后將您重定向到https://oidcdebugger.com/debug頁面,您可以在其中復(fù)制令牌。
如果愿意,可以轉(zhuǎn)到j(luò)sonwebtoken.io并解碼新令牌。 在有效內(nèi)容中, 子聲明將顯示用戶的電子郵件/用戶名,而組聲明將僅顯示“ 所有人”組。
{..."sub": "test@gmail.com","groups": ["Everyone"] }如果使用新令牌在/kayaks端點(diǎn)上發(fā)出請(qǐng)求,則會(huì)收到403 / Access Denied。
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'HTTP/1.1 403 ...{"error": "access_denied","error_description": "Access is denied" }為了演示@PreAuthorize批注的真正功能,請(qǐng)創(chuàng)建一個(gè)方法級(jí)別的安全約束。 將KayakRepository類更改為以下內(nèi)容:
@RepositoryRestResource public interface KayakRepository extends CrudRepository<Kayak, Long> { @PreAuthorize("hasAuthority('Admin')") <S extends Kayak> S save(S entity); }這僅將save()方法限制為Admin組的成員。 僅需身份驗(yàn)證即可限制存儲(chǔ)庫的其余部分,而無需特定的組成員身份。
重新啟動(dòng)Spring Boot服務(wù)器。 再次運(yùn)行相同的請(qǐng)求。
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'HTTP/1.1 200 ...{"_embedded": {"kayaks": []},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}} }_.embedded.kayaks存儲(chǔ)庫為空,因此_.embedded.kayaks是一個(gè)空數(shù)組。
嘗試創(chuàng)建一個(gè)新的皮劃艇。
http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H" "Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX..."您將獲得另一個(gè)403。“保存”在此處等于HTML POST。
但是,如果您使用從原始管理員帳戶生成的令牌,則可以使用。
注意:您的令牌可能已過期,您必須再次注銷developer.okta.com,然后在OIDC調(diào)試器上重新生成令牌。
使用您的管理員帳戶生成的令牌發(fā)布新的皮劃艇。
這次您將獲得201。
HTTP/1.1 201 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/json;charset=UTF-8 ...{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/1"},"self": {"href": "http://localhost:8080/kayaks/1"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500 }成功!
看一下Spring Data的CrudRepository接口,以了解可以被覆蓋的方法以及分配給方法級(jí)安全性的方法。 @PreAuthorize注釋不僅可以用于組,還可以使用更多的內(nèi)容。 可以利用Spring的表達(dá)語言(SpEL)的全部功能。
public interface CrudRepository<T, ID> extends Repository<T, ID> {<S extends T> S save(S entity);<S extends T> Iterable<S> saveAll(Iterable<S> entities);Optional<T> findById(ID id);boolean existsById(ID id);Iterable<T> findAll();Iterable<T> findAllById(Iterable<ID> ids);long count();void deleteById(ID id);void delete(T entity);void deleteAll(Iterable<? extends T> entities);void deleteAll(); }就是這樣! 很酷吧? 在本教程中,您將建立一個(gè)PostgreSQL數(shù)據(jù)庫,創(chuàng)建一個(gè)Spring Boot資源服務(wù)器,該服務(wù)器使用Spring Data和JPA來持久化數(shù)據(jù)模型,然后將該數(shù)據(jù)模型轉(zhuǎn)換為REST API,只需很少的代碼。 此外,您還使用Okta向服務(wù)器應(yīng)用程序添加了OIDC身份驗(yàn)證和OAuth 2.0授權(quán)。 最后,您實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的基于組的授權(quán)方案。
如果您想查看這個(gè)完整的項(xiàng)目,可以在GitHub上的倉庫中找到@ oktadeveloper / okta-spring-boot-jpa-example 。
請(qǐng)留意本系列中的下一篇文章,該文章將介紹在Spring WebFlux中使用NoSQL數(shù)據(jù)庫(MongoDB)。
了解有關(guān)Spring Boot,Spring Security和安全身份驗(yàn)證的更多信息
如果您想了解有關(guān)Spring Boot,Spring Security或現(xiàn)代應(yīng)用程序安全性的更多信息,請(qǐng)查看以下任何出色的教程:
- Spring Boot,OAuth 2.0和Okta入門
- 15分鐘內(nèi)將單一登錄添加到您的Spring Boot Web App
- 使用多重身份驗(yàn)證保護(hù)您的Spring Boot應(yīng)用程序安全
- 使用Spring Boot和GraphQL構(gòu)建安全的API
如果您想深入研究,請(qǐng)查看Okta Spring Boot Starter GitHub項(xiàng)目 。
這對(duì)于Spring Data和保護(hù)Spring Boot項(xiàng)目是一個(gè)很好的參考: https : //docs.spring.io/spring-data/rest/docs/current/reference/html/
當(dāng)將PostgreSQL與JPA和Hibernate一起使用時(shí), Vlad Mihalcea有一個(gè)很棒的教程,標(biāo)題為《 9個(gè)高性能技巧》 。
Baeldung有一個(gè)關(guān)于保護(hù)Spring Data / Spring Boot項(xiàng)目中的方法的有用教程: https ://www.baeldung.com/spring-security-method-security
最后,如果您需要在Mac OS X上使用PostgreSQL的更多幫助,請(qǐng)參閱此codementor.io教程 。
如果您對(duì)此帖子有任何疑問,請(qǐng)?jiān)谙旅嫣砑釉u(píng)論。 有關(guān)更多精彩內(nèi)容, 請(qǐng)?jiān)赥witter上關(guān)注@oktadev , 在Facebook上關(guān)注我們,或訂閱我們的YouTube頻道 。
``使用PostgreSQL使用Spring Boot和JPA構(gòu)建應(yīng)用程序''最初于2018年12月13日發(fā)布在Okta開發(fā)者博客上。
“我喜歡編寫身份驗(yàn)證和授權(quán)代碼。” ?從來沒有Java開發(fā)人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進(jìn)行托管身份驗(yàn)證,授權(quán)和多因素身份驗(yàn)證。
翻譯自: https://www.javacodegeeks.com/2018/12/build-basic-spring-boot-using-postgresql.html
總結(jié)
以上是生活随笔為你收集整理的使用PostgreSQL使用Spring Boot和JPA构建基本应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网件ddns(网件ddos)
- 下一篇: Spring Boot集成测试中@Con