2020-11-21 更新:解決由于.yml文件引起的數(shù)據(jù)庫(kù)鏈接問題
文章目錄 一、前言 二、項(xiàng)目環(huán)境 三、項(xiàng)目文件結(jié)構(gòu) 四、項(xiàng)目代碼 數(shù)據(jù)庫(kù)連接配置 1.Entity層 2.dao層 3.service層 4.controller層 五、運(yùn)行效果 六、參考資料
一、前言
為了避免浪費(fèi)時(shí)間進(jìn)行不必要的閱讀,這里先對(duì)項(xiàng)目進(jìn)行簡(jiǎn)單的介紹。在實(shí)際應(yīng)用場(chǎng)景中,每個(gè)用戶都有對(duì)應(yīng)的角色,而每個(gè)角色又有對(duì)應(yīng)的一些角色。因?yàn)橐粋€(gè)用戶可以有多個(gè)角色,一個(gè)角色也可以被多個(gè)用戶所擁有,角色和權(quán)限的關(guān)系也同理,這里主要利用多對(duì)多的映射關(guān)系 將他們聯(lián)系起來,對(duì)他們進(jìn)行管理。
主要實(shí)現(xiàn)的功能:
添加用戶、角色和權(quán)限 刪除用戶、角色和權(quán)限 給用戶添加角色、給角色添加權(quán)限 根據(jù)用戶名稱查詢用戶擁有的權(quán)限
二、項(xiàng)目環(huán)境
Java版本:jdk1.8.0_181 IDE:IntelliJ IDEA 2019.1.3 數(shù)據(jù)庫(kù):postgresql 9.5 測(cè)試工具:postman
ps:數(shù)據(jù)庫(kù)類型不同不要緊,在創(chuàng)建的時(shí)候勾選不一樣的數(shù)據(jù)庫(kù)驅(qū)動(dòng)的就行。
三、項(xiàng)目文件結(jié)構(gòu)
項(xiàng)目創(chuàng)建的時(shí)候,需要勾選Web中的Spring Web Starter 和SQL中Spring Data JPA、PostgreSQL Driver (如果使用的是mysql數(shù)據(jù)庫(kù),則勾選MySQL Driver),IDEA會(huì)自動(dòng)幫我們?cè)贛aven的配置文件中添加相關(guān)的依賴。
以下是本項(xiàng)目的目錄結(jié)構(gòu):
四、項(xiàng)目代碼
數(shù)據(jù)庫(kù)連接配置
spring : datasource : driver-class-name : org.postgresql.Driver
username : postgres
password : 123456 url : jdbc
: postgresql
: //localhost
: 5432/postgres
jpa : hibernate : ddl-auto : update
show-sql : true properties : hibernate : temp : use_jdbc_metadata_defaults : false
如果遇到數(shù)據(jù)庫(kù)連接不成功的問題,可以嘗試將 properties: 以及之后的部分刪除。
1.Entity層
Entity層為數(shù)據(jù)庫(kù)實(shí)體層,一般一個(gè)實(shí)體類對(duì)應(yīng)數(shù)據(jù)庫(kù)中的一張數(shù)據(jù)表,類中的屬性與數(shù)據(jù)表中的字段一 一對(duì)應(yīng)。默認(rèn)情況下,類名即為數(shù)據(jù)表的表名,屬性名則是對(duì)應(yīng)字段名,字段類型也與變量的類型相對(duì)應(yīng)。
本層注解簡(jiǎn)單解釋:
@Entity 該注解用于表明這個(gè)類是一個(gè)實(shí)體類,會(huì)給他生成一張對(duì)應(yīng)的數(shù)據(jù)表。 @Table(name = “table_name”) 該注解主要用于修改表名,name的值就是修改的數(shù)據(jù)表的名稱。 @Id 該注解用于聲明主鍵,標(biāo)在哪個(gè)屬性上面對(duì)應(yīng)的哪個(gè)字段就是主鍵 @GeneratedValue(strategy = GenerationType.IDENTITY) 該注解的strategy屬性主要用于設(shè)置主鍵的增長(zhǎng)方式,IDENTITY表示主鍵由數(shù)據(jù)庫(kù)自己生成,從1開始單調(diào)遞增。 @Column(name = “column_name”) 該注解的name屬性用于更改數(shù)據(jù)表的列名,如果不想用默認(rèn)的就用這個(gè)屬性改吧 @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 這個(gè)注解得上是本項(xiàng)目得核心了,它聲明了實(shí)體之間的多對(duì)多關(guān)系,使兩張數(shù)據(jù)表關(guān)聯(lián)關(guān)聯(lián)起來,一般是通過生成一張映射表來實(shí)現(xiàn)這種映射關(guān)系 。關(guān)于上面的cascade屬性和fetch屬性,有興趣的讀者可以查資料了解。 @JoinTable 這個(gè)注解是配套@ManyToMany使用的,一般在多對(duì)多關(guān)系的維護(hù)端標(biāo)注,用于生成上面提到的映射表。一般該注解常用三個(gè)屬性:name屬性表示生成的數(shù)據(jù)表的名稱,joinColumns屬性表示關(guān)系維護(hù)端的主鍵,inverseJoinColumns則表示關(guān)系被維護(hù)端的主鍵 。關(guān)于嵌套在里面的@JoinColumn注解,在這里主要用于配置映射表的外鍵,一般有兩個(gè)屬性:name用于配置外鍵在映射表中的名稱,referencedColumnName 用于表明外鍵在原表中的字段名稱。@JsonBackReference 關(guān)于這個(gè)注解,建議先去掉試試然后再加上,對(duì)比一下效果。它主要可以使標(biāo)注屬性避免被json序列化 ,進(jìn)而避免多對(duì)多關(guān)系的查詢中出現(xiàn)死循環(huán)的情況。但是加上了這注解后,就不能進(jìn)行反向查詢了(也就是說不能利用權(quán)限名查詢擁有這個(gè)權(quán)限的角色了)
注意:以下代碼都省略了要導(dǎo)入的包,getter和setter方法。需要導(dǎo)入相關(guān)包可以用快捷鍵Alt+Insert,用快捷鍵Alt+Insert然后選擇Getter and Setter可以快速生成相關(guān)方法。
@Entity
@Table ( name
= "user_tabel" )
public class User { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "user_id" ) private Integer userId
; @Column ( name
= "user_name" ) private String userName
; @ManyToMany ( cascade
= CascadeType
. ALL
, fetch
= FetchType
. LAZY
) @JoinTable ( name
= "user_role" , joinColumns
= { @JoinColumn ( name
= "user_id" ) } , inverseJoinColumns
= { @JoinColumn ( name
= "role_id" ) } ) private List
< Role> roles
;
}
@Entity
@Table ( name
= "role_table" )
public class Role { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "role_id" ) private Integer roleId
; @Column ( name
= "role_name" ) private String roleName
; @JsonBackReference @ManyToMany ( mappedBy
= "roles" ) private List
< User> users
; @ManyToMany ( cascade
= CascadeType
. ALL
, fetch
= FetchType
. LAZY
) @JoinTable ( name
= "role_auth" , joinColumns
= @JoinColumn ( name
= "role_id" ) , inverseJoinColumns
= @JoinColumn ( name
= "auth_id" ) ) private List
< Authority> authorities
;
}
@Entity
@Table ( name
= "auth_table" )
public class Authority { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "auth_id" ) private Integer authorityId
; @Column ( name
= "auth_name" ) private String authorityName
; @JsonBackReference @ManyToMany ( mappedBy
= "authorities" ) private List
< Role> roles
;
}
2.dao層
dao層是數(shù)據(jù)持久層,也被稱為mapper層。主要負(fù)責(zé)訪問數(shù)據(jù)庫(kù),向數(shù)據(jù)庫(kù)發(fā)送SQL語句,完成基礎(chǔ)的增刪查改任務(wù)。主要通過定義繼承JpaRepository類的接口來實(shí)現(xiàn),<>中填寫的是實(shí)體類的名稱和該實(shí)體主鍵的變量類型。
在接口中聲明的方法不用我們?nèi)?shí)現(xiàn),只要滿足命名規(guī)則JpaRepository類會(huì)自動(dòng)幫我們生成相應(yīng)的sql語句 。 詳情見:官方文檔
public interface UserRepository extends JpaRepository < User, Integer> { public List
< User> findAllByUserName ( String userName
) ; public void deleteByUserName ( String userName
) ;
}
public interface RoleRepository extends JpaRepository < Role, Integer> { public List
< Role> findAllByRoleName ( String roleName
) ; public void deleteByRoleName ( String roleName
) ;
}
public interface AuthorityRepository extends JpaRepository < Authority, Integer> { public List
< Authority> findAllByAuthorityName ( String authorityName
) ; public void deleteByAuthorityName ( String authorityName
) ;
}
3.service層
service層是業(yè)務(wù)邏輯層,主要通過調(diào)用dao層的接口,接收dao層返回的數(shù)據(jù),完成項(xiàng)目的基本功能設(shè)計(jì) 。由于本項(xiàng)目的service層是在后面才加的,所以有些應(yīng)該在本層實(shí)現(xiàn)的功能寫在了controller層orz。
踩到的坑
涉及到兩張表以上的更新或者刪除操作,為了保證數(shù)據(jù)庫(kù)的一致性,需要添加 @Transactional事務(wù)注解,否則程序會(huì)拋出異常。(關(guān)于事務(wù)的詳情,如果不熟悉的話,強(qiáng)烈建議去弄懂。) 如果要執(zhí)行刪除操作,需要先把它的List先清空,也就相當(dāng)于把映射表中的關(guān)系清除。否則會(huì)拋出org.hibernate.exception.ConstraintViolationException異常。(我這里用到了多種清除方式:如果刪除維護(hù)端數(shù)據(jù),只是把維護(hù)端的List清空就行;如果刪除被維護(hù)端的數(shù)據(jù),則把用戶(維護(hù)端)的List中要移除的角色(被維護(hù)端)都remove掉,不知道我是不是想多了)
@Service
public class EntityService { @Autowired private UserRepository userRepository
; @Autowired private RoleRepository roleRepository
; @Autowired private AuthorityRepository authorityRepository
; @Transactional public void deleteUser ( String userName
) { List
< User> users
= userRepository
. findAllByUserName ( userName
) ; for ( User user
: users
) { user
. getRoles ( ) . clear ( ) ; userRepository
. save ( user
) ; } userRepository
. deleteByUserName ( userName
) ; } @Transactional public void deleteRole ( String roleName
) { List
< Role> roles
= roleRepository
. findAllByRoleName ( roleName
) ; List
< User> users
= userRepository
. findAll ( ) ; for ( User user
: users
) { List
< Role> userRole
= user
. getRoles ( ) ; for ( Role role
: roles
) { if ( userRole
. contains ( role
) ) { userRole
. remove ( role
) ; } role
. getAuthorities ( ) . clear ( ) ; roleRepository
. save ( role
) ; } userRepository
. save ( user
) ; } roleRepository
. deleteByRoleName ( roleName
) ; } @Transactional public void deleteAuthority ( String authName
) { List
< Authority> authorities
= authorityRepository
. findAllByAuthorityName ( authName
) ; List
< Role> roles
= roleRepository
. findAll ( ) ; for ( Role role
: roles
) { List
< Authority> roleAuthoritis
= role
. getAuthorities ( ) ; for ( Authority authority
: authorities
) { if ( roleAuthoritis
. contains ( authority
) ) { roleAuthoritis
. remove ( authority
) ; } } roleRepository
. save ( role
) ; } authorityRepository
. deleteByAuthorityName ( authName
) ; } }
4.controller層
controller層是控制層,其功能為請(qǐng)求和響應(yīng)控制,負(fù)責(zé)前后端交互,接受前端請(qǐng)求,調(diào)用service層,接收service層返回的數(shù)據(jù),最后返回具體的頁面和數(shù)據(jù)到客戶端。
本層注解簡(jiǎn)單解釋:
@RestController Spring4之后新加入的注解,相當(dāng)于@Controller + @ResponseBody。 @Controller 將當(dāng)前修飾的類注入SpringBoot IOC容器,使得從該類所在的項(xiàng)目跑起來的過程中,這個(gè)類就被實(shí)例化。當(dāng)然也有語義化的作用,即代表該類是充當(dāng)Controller的作用 @ResponseBody 它的作用簡(jiǎn)單來說說就是指該類中所有的API接口返回的數(shù)據(jù),甭管你對(duì)應(yīng)的方法返回Map或是其他Object,它會(huì)以Json字符串的形式返回給客戶端,根據(jù)嘗試,如果返回的是String類型,則仍然是String。
@RequestMapping("/user") 該注解用來處理請(qǐng)求地址的映射,可用于類或方法上。用于類上,表示類中的所有響應(yīng)請(qǐng)求的方法都是以該地址作為父路徑。
@Autowired 養(yǎng)成看源代碼的好習(xí)慣,在IDEA中按住Ctrl鍵點(diǎn)擊該注解,可以查看該注解的解析。我理解了一下,大概就是調(diào)用這個(gè)類的構(gòu)造方法對(duì)這個(gè)類進(jìn)行實(shí)例化操作。
@RequestParam(value = “userName”) 該注解可以獲取請(qǐng)求報(bào)文中的數(shù)據(jù)(數(shù)據(jù)一般以鍵值對(duì)方式傳輸),把然后把獲取到的數(shù)據(jù)復(fù)制給方法的參數(shù)中。例如上面就是獲取名為"userName"的數(shù)據(jù)值。
再簡(jiǎn)單介紹一下,增加用戶、角色和權(quán)限的操作。一般我們添加的時(shí)候,是先添加權(quán)限,再添加角色,最后添加角色 (可以聯(lián)想一下,是不是先有權(quán)限才能給角色分配呀)。有些人會(huì)對(duì)如何關(guān)聯(lián)用戶和角色、角色和權(quán)限有疑惑(包括一開始的自己),在實(shí)體類中存在一個(gè)List對(duì)象,只要在其中添加對(duì)應(yīng)的對(duì)象映射表中就會(huì)創(chuàng)建好映射關(guān)系。 (可以看看下面添加的代碼,然后自己做實(shí)驗(yàn)觀察現(xiàn)象)
只要這個(gè)不是你的第一個(gè)spring boot程序,相信你都看得懂。如果感覺功能不夠,讀者還可以自行添加。
@RestController
@RequestMapping ( "/user" )
public class EntityController { @Autowired private UserRepository userRepository
; @Autowired private RoleRepository roleRepository
; @Autowired private AuthorityRepository authorityRepository
; @Autowired private EntityService entityService
; @RequestMapping ( "/finduser" ) public List
< User> findByName ( @RequestParam ( value
= "userName" ) String userName
) { return userRepository
. findAllByUserName ( userName
) ; } @RequestMapping ( "/findalluser" ) public List
< User> findAllUser ( ) { return userRepository
. findAll ( ) ; } @RequestMapping ( "/adduser" ) public List
< User> addUser ( @RequestParam ( value
= "userName" ) String userName
, @RequestParam ( value
= "roleName" ) String roleName
) { User user
= new User ( ) ; Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; user
. setUserName ( userName
) ; user
. setRoles ( new ArrayList < > ( ) ) ; user
. getRoles ( ) . add ( role
) ; userRepository
. save ( user
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/adduserrole" ) public List
< User> addUserRole ( @RequestParam ( value
= "userName" ) String userName
, @RequestParam ( value
= "roleName" ) String roleName
) { User user
= userRepository
. findAllByUserName ( userName
) . get ( 0 ) ; Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; if ( user
. getRoles ( ) == null
) { user
. setRoles ( new ArrayList < > ( ) ) ; } user
. getRoles ( ) . add ( role
) ; userRepository
. save ( user
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/deleteuser" ) public List
< User> deleteUser ( @RequestParam ( value
= "userName" ) String userName
) { entityService
. deleteUser ( userName
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/getauth" ) public Set
< Authority> getAuthority ( @RequestParam ( value
= "userName" ) String userName
) { Set
< Authority> authoritieSet
= new HashSet < > ( ) ; User user
= userRepository
. findAllByUserName ( userName
) . get ( 0 ) ; for ( Role role
: user
. getRoles ( ) ) { for ( Authority authority
: role
. getAuthorities ( ) ) { authoritieSet
. add ( authority
) ; } } return authoritieSet
; } @RequestMapping ( "/findallrole" ) public List
< Role> findAllRole ( ) { return roleRepository
. findAll ( ) ; } @RequestMapping ( "/addrole" ) public List
< Role> addRole ( @RequestParam ( value
= "roleName" ) String roleName
, @RequestParam ( value
= "authName" ) String authName
) { Role role
= new Role ( ) ; Authority authority
= authorityRepository
. findAllByAuthorityName ( authName
) . get ( 0 ) ; role
. setRoleName ( roleName
) ; role
. setAuthorities ( new ArrayList < > ( ) ) ; role
. getAuthorities ( ) . add ( authority
) ; roleRepository
. save ( role
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/addroleauth" ) public List
< Role> addRoleAuth ( @RequestParam ( value
= "roleName" ) String roleName
, @RequestParam ( value
= "authName" ) String authName
) { Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; Authority authority
= authorityRepository
. findAllByAuthorityName ( authName
) . get ( 0 ) ; if ( role
. getAuthorities ( ) == null
) { role
. setAuthorities ( new ArrayList < > ( ) ) ; } role
. getAuthorities ( ) . add ( authority
) ; roleRepository
. save ( role
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/deleterole" ) public List
< Role> deleteRole ( @RequestParam ( value
= "roleName" ) String roleName
) { entityService
. deleteRole ( roleName
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/findallauth" ) public List
< Authority> findAllAuthority ( ) { return authorityRepository
. findAll ( ) ; } @RequestMapping ( "/addauth" ) public List
< Authority> addAuthority ( @RequestParam ( value
= "authName" ) String authName
) { Authority authority
= new Authority ( ) ; authority
. setAuthorityName ( authName
) ; authorityRepository
. save ( authority
) ; return authorityRepository
. findAll ( ) ; } @RequestMapping ( "/deleteauth" ) public List
< Authority> deletAuthority ( @RequestParam ( value
= "authName" ) String authName
) { entityService
. deleteAuthority ( authName
) ; return authorityRepository
. findAll ( ) ; }
}
五、運(yùn)行效果
寫得函數(shù)有點(diǎn)多,這里挑選一部分來演示吧。
數(shù)據(jù)表 在程序運(yùn)行之后,它會(huì)自動(dòng)為我們?cè)跀?shù)據(jù)庫(kù)中創(chuàng)建5張表,其中包括3個(gè)實(shí)體對(duì)應(yīng)的數(shù)據(jù)表以及2張映射表。
查詢操作 由于先前已經(jīng)進(jìn)行了一些實(shí)驗(yàn),數(shù)據(jù)表中已經(jīng)有了少量的數(shù)據(jù),所以我們就現(xiàn)在演示查詢吧。 首先按照上文說的添加順序,先是權(quán)限的查詢。 接著是角色的查詢: 接著是用戶查詢: 最后,我們通過用戶名來查詢他擁有的權(quán)限。
增加角色操作 添加權(quán)限的操作很常規(guī)不做演示,添加用戶的操作和添加角色的操作差不多可以借鑒。
六、參考資料
https://www.cnblogs.com/hhhshct/p/9492741.html https://liuyanzhao.com/7913.html https://blog.csdn.net/lidai352710967/article/details/83509821 https://blog.csdn.net/H_Shun/article/details/78013017 https://blog.csdn.net/Just_learn_more/article/details/90665009 https://www.sojson.com/blog/295.html https://www.jianshu.com/p/6bbb5748ac83
總結(jié)
以上是生活随笔 為你收集整理的Spring Boot实现简单的用户权限管理(超详细版) 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。