close

點擊關注公眾號,Java乾貨及時送達



系統權限管理1、前言

在實際開發中,開發任何一套系統,基本都少不了權限管理這一塊。這些足以說明權限管理的重要性。其實SpringSecurity去年就學了,一直沒有時間整理,用了一年多時間了,給我的印象一直都挺好,實用,安全性高(Security可以對密碼進行加密)。而且這一塊在實際開發中也的確很重要,所以這裡整理了一套基於SpringSecurity的權限管理。

案例代碼下面有下載鏈接。

2、案例技術棧

如果對於SpringSecurity還不了解的話可以先了解一下SpringSecurity安全控件的學習,頁面採用的是Bootstrap寫的(頁面就簡單的寫了一下,可以根據自己的需求更改),其實後端理解了,前台就是顯示作用,大家可以自行更換前台頁面顯示框架,持久層使用的是Spring-Data-Jpa。

並且對後端持久層和控制器進行了一下小封裝,Java持久層和控制器的封裝。頁面使用的Thymeleaf模板,SpringBoot整合Thymeleaf模板。

數據庫設計1、表關係

菜單(TbMenu)=====> 頁面上需要顯示的所有菜單

角色(SysRole)=====> 角色及角色對應的菜單

用戶(SysUser)=====> 用戶及用戶對應的角色

用戶和角色中間表(sys_user_role)====> 用戶和角色中間表

2、數據庫表結構

菜單表tb_menu

角色及菜單權限表sys_role,其中父節點parent 為null時為角色,不為null時為對應角色的菜單權限。

用戶表sys_user

用戶和角色多對多關係,用戶和角色中間表sys_user_role(有Spring-Data-Jpa自動生成)。

新建項目1、新建springboot項目

新建springboot項目,在項目中添加SpringSecurity相關Maven依賴,pom.map文件

<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="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.0https://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.2.2.RELEASE</version><relativePath/><!--lookupparentfromrepository--></parent><groupId>com.mcy</groupId><artifactId>springboot-security</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-security</name><description>DemoprojectforSpringBoot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></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><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.webjars.bower</groupId><artifactId>bootstrap-select</artifactId><version>2.0.0-beta1</version></dependency><dependency><groupId>org.webjars</groupId><artifactId>bootbox</artifactId><version>4.4.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>2、項目結構編寫代碼1、編寫實體類

菜單表實體類TbMenu,Spring-Data-Jpa可以根據實體類去數據庫新建或更新對應的表結構,詳情可以訪問Spring-Data-Jpa入門:

https://blog.csdn.net/qq_40205116/article/details/103039936

importcom.fasterxml.jackson.annotation.JsonIgnore;importcom.mcy.springbootsecurity.custom.BaseEntity;importorg.springframework.data.annotation.CreatedBy;importjavax.persistence.*;importjava.util.ArrayList;importjava.util.List;/***菜單表*@author**/@EntitypublicclassTbMenuextendsBaseEntity<Integer>{privateStringname;privateStringurl;privateIntegeridx;@JsonIgnoreprivateTbMenuparent;@JsonIgnoreprivateList<TbMenu>children=newArrayList<>();@Column(unique=true)publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetUrl(){returnurl;}publicvoidsetUrl(Stringurl){this.url=url;}publicIntegergetIdx(){returnidx;}publicvoidsetIdx(Integeridx){this.idx=idx;}@ManyToOne@CreatedBypublicTbMenugetParent(){returnparent;}publicvoidsetParent(TbMenuparent){this.parent=parent;}@OneToMany(cascade=CascadeType.ALL,mappedBy="parent")@OrderBy(value="idx")publicList<TbMenu>getChildren(){returnchildren;}publicvoidsetChildren(List<TbMenu>children){this.children=children;}publicTbMenu(Integerid){super(id);}publicTbMenu(){super();}publicTbMenu(Stringname,Stringurl,Integeridx,TbMenuparent,List<TbMenu>children){this.name=name;this.url=url;this.idx=idx;this.parent=parent;this.children=children;}publicTbMenu(Integerinteger,Stringname,Stringurl,Integeridx,TbMenuparent,List<TbMenu>children){super(integer);this.name=name;this.url=url;this.idx=idx;this.parent=parent;this.children=children;}@TransientpublicIntegergetParentId(){returnparent==null?null:parent.getId();}}

表新建好了,下面就是實現增刪改查就可以了,實現效果如下。

新增和修改菜單。

對於Bootstrap的樹形表格,可以移步到:BootStrap-bable-treegrid樹形表格的使用。

https://blog.csdn.net/qq_40205116/article/details/103740104

菜單管理實現了,下一步就是實現角色及角色對應的權限管理了。

角色及權限表SysRole,parent 為null時為角色,不為null時為權限。

packagecom.mcy.springbootsecurity.entity;importcom.fasterxml.jackson.annotation.JsonIgnore;importcom.mcy.springbootsecurity.custom.BaseEntity;importorg.springframework.data.annotation.CreatedBy;importjavax.persistence.*;importjava.util.ArrayList;importjava.util.List;@Entity/****角色及角色對應的菜單權限*@author*parent為null時為角色,不為null時為權限*/publicclassSysRoleextendsBaseEntity<Integer>{privateStringname;//名稱privateStringcode;//代碼@JsonIgnoreprivateSysRoleparent;privateIntegeridx;//排序@JsonIgnoreprivateList<SysRole>children=newArrayList<>();@Column(length=20)publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetCode(){returncode;}publicvoidsetCode(Stringcode){this.code=code;}@ManyToOne@CreatedBypublicSysRolegetParent(){returnparent;}publicvoidsetParent(SysRoleparent){this.parent=parent;}@OneToMany(cascade=CascadeType.ALL,mappedBy="parent")publicList<SysRole>getChildren(){returnchildren;}publicvoidsetChildren(List<SysRole>children){this.children=children;}//獲取父節點id@TransientpublicIntegergetParentId(){returnparent==null?null:parent.getId();}publicIntegergetIdx(){returnidx;}publicvoidsetIdx(Integeridx){this.idx=idx;}publicSysRole(Stringname,Stringcode,SysRoleparent,Integeridx,List<SysRole>children){this.name=name;this.code=code;this.parent=parent;this.idx=idx;this.children=children;}publicSysRole(Integerid,Stringname,Stringcode,SysRoleparent,Integeridx,List<SysRole>children){super(id);this.name=name;this.code=code;this.parent=parent;this.idx=idx;this.children=children;}publicSysRole(Integerid){super(id);}publicSysRole(){}}

首先需要實現角色管理,之後在角色中添加對應的菜單權限。

實現效果(也可以和菜單管理一樣,用樹形表格展示,根據個人需求。這裡用的是樹形菜單展示的)。

給角色分配權限。

最後實現的就是用戶管理了,只需要對添加的用戶分配對應的角色就可以了,用戶登錄時,顯示角色對應的權限。

用戶表SysUser,繼承的BaseEntity類中就一個ID字段。

importcom.fasterxml.jackson.annotation.JsonIgnore;importcom.mcy.springbootsecurity.custom.BaseEntity;importjavax.persistence.*;importjava.util.ArrayList;importjava.util.List;/***用戶表*/@EntitypublicclassSysUserextendsBaseEntity<Integer>{privateStringusername;//賬號privateStringpassword;//密碼privateStringname;//姓名privateStringaddress;//地址@JsonIgnoreprivateList<SysRole>roles=newArrayList<>();//角色@Column(length=20,unique=true)publicStringgetUsername(){returnusername;}publicvoidsetUsername(Stringusername){this.username=username;}@Column(length=100)publicStringgetPassword(){returnpassword;}publicvoidsetPassword(Stringpassword){this.password=password;}@Column(length=20)publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}@ManyToMany(cascade=CascadeType.REFRESH,fetch=FetchType.EAGER)@JoinTable(name="sys_user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))publicList<SysRole>getRoles(){returnroles;}publicvoidsetRoles(List<SysRole>roles){this.roles=roles;}publicStringgetAddress(){returnaddress;}publicvoidsetAddress(Stringaddress){this.address=address;}//角色名稱@TransientpublicStringgetRoleNames(){Stringstr="";for(SysRolerole:getRoles()){str+=role.getName()+",";}if(str.length()>0){str=str.substring(0,str.length()-1);}returnstr;}//角色代碼@TransientpublicStringgetRoleCodes(){Stringstr="";for(SysRolerole:getRoles()){str+=role.getCode()+",";}if(str.indexOf(",")>0){str=str.substring(0,str.length()-1);}returnstr;}}

用戶管理就基本的數據表格,效果如圖。

2、Security配置文件

Security相關配置文件,下面兩個文件如果看不懂,可以訪問SpringSecurity安全控件的學習中有詳細講解。

https://blog.csdn.net/qq_40205116/article/details/103439326

packagecom.mcy.springbootsecurity.security;importcom.mcy.springbootsecurity.service.SysUserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@ConfigurationpublicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{@AutowiredprivateSysUserServiceuserService;/***用戶認證操作*@paramauth*@throwsException*/@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{//添加用戶,並給予權限auth.inMemoryAuthentication().withUser("aaa").password("{noop}1234").roles("DIY");//設置認證方式auth.userDetailsService(userService).passwordEncoder(newBCryptPasswordEncoder());}/***用戶授權操作*@paramhttp*@throwsException*/@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();//安全器令牌http.formLogin()//登錄請求被攔截.loginPage("/login").permitAll()//設置默認登錄成功跳轉頁面.successForwardUrl("/main").failureUrl("/login?error");//登錄失敗的頁面http.authorizeRequests().antMatchers("/static/**","/assets/**").permitAll();//文件下的所有都能訪問http.authorizeRequests().antMatchers("/webjars/**").permitAll();http.logout().logoutUrl("/logout").permitAll();//退出http.authorizeRequests().anyRequest().authenticated();//除此之外的都必須通過請求驗證才能訪問}}

獲取登錄者相關信息,工具類。

importcom.mcy.springbootsecurity.entity.SysUser;importcom.mcy.springbootsecurity.service.SysUserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.stereotype.Component;importjava.util.ArrayList;importjava.util.List;//創建會話,獲取當前登錄對象@ComponentpublicclassUserUtils{@AutowiredprivateSysUserServiceuserService;/***獲取當前登錄者的信息*@return當前者信息*/publicSysUsergetUser(){//獲取當前用戶的用戶名Stringusername=SecurityContextHolder.getContext().getAuthentication().getName();SysUseruser=userService.findByUsername(username);returnuser;}/***判斷此用戶中是否包含roleName菜單權限*@paramroleName*@return*/publicBooleanhasRole(StringroleName){//獲取UserDetails類,UserDetailsuserDetails=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();List<String>roleCodes=newArrayList<>();for(GrantedAuthorityauthority:userDetails.getAuthorities()){//getAuthority()返回用戶對應的菜單權限roleCodes.add(authority.getAuthority());}returnroleCodes.contains(roleName);}}3、動態權限菜單加載相關方法

用戶表的SysUserService需要實現UserDetailsService接口,因為在SpringSecurity中配置的相關參數需要是UserDetailsService類的數據。

重寫UserDetailsService接口中的loadUserByUsername方法,通過該方法查詢對應的用戶,返回對象UserDetails是SpringSecurity的一個核心接口。其中定義了一些可以獲取用戶名,密碼,權限等與認證相關信息的方法。

重寫的loadUserByUsername方法。

@OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{//調用持久層接口findByUsername方法查詢用戶。SysUseruser=userRepository.findByUsername(username);if(user==null){thrownewUsernameNotFoundException("用戶名不存在");}//創建List集合,用來保存用戶菜單權限,GrantedAuthority對象代表賦予當前用戶的權限List<GrantedAuthority>authorities=newArrayList<>();//獲得當前用戶角色集合List<SysRole>roles=user.getRoles();List<SysRole>haveRoles=newArrayList<>();for(SysRolerole:roles){haveRoles.add(role);List<SysRole>children=roleService.findByParent(role);children.removeAll(haveRoles);haveRoles.addAll(children);}for(SysRolerole:haveRoles){//將關聯對象role的name屬性保存為用戶的認證權限authorities.add(newSimpleGrantedAuthority(role.getName()));}//此處返回的是org.springframework.security.core.userdetails.User類,該類是SpringSecurity內部的實現//org.springframework.security.core.userdetails.User類實現了UserDetails接口returnnewUser(user.getUsername(),user.getPassword(),authorities);}

所有功能實現了,最後就是根據角色去顯示對應的菜單了。

在TbMenuService類中的findAuditMenu方法,查詢當前用戶所擁有的權限菜單。

/***獲取用戶所擁有的權限對應的菜單項*@return*/publicList<TbMenu>findAuditMenu(){List<TbMenu>menus;//判斷是否是後門用戶if(userUtils.hasRole("ROLE_DIY")){//查詢所有菜單,子菜單可以通過父級菜單的映射得到menus=menuRepository.findByParentIsNullOrderByIdx();}else{//獲取此用戶對應的菜單權限menus=auditMenu(menuRepository.findByParentIsNullOrderByIdx());}returnmenus;}//根據用戶的菜單權限對菜單進行過濾privateList<TbMenu>auditMenu(List<TbMenu>menus){List<TbMenu>list=newArrayList<>();for(TbMenumenu:menus){Stringname=menu.getName();//判斷此用戶是否有此菜單權限if(userUtils.hasRole(name)){list.add(menu);//遞歸判斷子菜單if(menu.getChildren()!=null&&!menu.getChildren().isEmpty()){menu.setChildren(auditMenu(menu.getChildren()));}}}returnlist;}

在UserUtils工具類中的hasRole方法,判斷此用戶中是否包含roleName菜單權限。

publicBooleanhasRole(StringroleName){//獲取UserDetails類,UserDetailsuserDetails=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();List<String>roleCodes=newArrayList<>();for(GrantedAuthorityauthority:userDetails.getAuthorities()){//getAuthority()返回用戶對應的菜單權限roleCodes.add(authority.getAuthority());}returnroleCodes.contains(roleName);}

之後在控制器中返回用戶對應的菜單權限,之後在前台頁面遍歷就可以了。

@RequestMapping(value="/main")publicStringmain(ModelMapmap){//加載菜單List<TbMenu>menus=menuService.findAuditMenu();map.put("menus",menus);if(menus.isEmpty()){return"main/main";}return"main/main1";}4、首頁菜單遍歷

首頁菜單遍歷,這裡使用的是LayUI菜單,如果其他框架可以自行根據頁面標籤規律遍歷,因為頁面使用的是Thymeleaf模板,不是JSP,使用遍歷菜單時不是採用的EL表達式,而是使用的Thymeleaf自帶的標籤表達式。

<divid="main"><divid="main_nav"><divclass="panel-group"id="accordion"style="margin-bottom:0;"><divth:each="menu,menuStat:${menus}"th:if="${menu.children.size()!=0&&menu.children!=null}"class="panelpanel-default"><divclass="panel-heading"><h4class="panel-title"><pdata-toggle="collapse"data-parent="#accordion"th:href="|#collapseOne${menuStat.index}|"><span th:text="${menu.name}">系統設置</span><span class="caret"></span></p></h4></div><divth:if="${menuStat.first}"th:id="|collapseOne${menuStat.index}|"class="panel-collapsecollapsecollapsein"><divclass="panel-body"><pth:each="subMenu:${menu.children}"th:src="${subMenu.url}"th:text="${subMenu.name}">菜單管理</p></div></div><divth:if="${!menuStat.first}"th:id="|collapseOne${menuStat.index}|"class="panel-collapsecollapsecollapse"><divclass="panel-body"><pth:each="subMenu:${menu.children}"th:src="${subMenu.url}"th:text="${subMenu.name}">菜單管理</p></div></div></div></div><divid="nav_p"><pth:each="menu:${menus}"th:if="${menu.children.size()==0}"th:src="${menu.url}"th:text="${menu.name}">成績管理</p></div></div><divid="main_home">首頁內容</div></div>測試應用1、對應效果展示

用戶數據及對應的角色

管理員對應的菜單權限。

用戶角色對應的菜單權限。

測試用戶角色對應的菜單權限。

2、測試應用

用戶名為admin1有管理員角色的用戶登錄,菜單顯示。

用戶名為admin2有用戶角色的用戶登錄,菜單顯示。

用戶名為admin3有測試用戶角色的用戶登錄,菜單顯示。

3、案例代碼下載

下載地址:https://github.com/machaoyin/SpringBoot-Security

來源:blog.csdn.net/qq_40205116/article/details/103739978

點分享

點收藏

點點讚

點在看

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()