前言
本篇博客主要分享: springSecurity源碼分析,權限表達式,基於數據庫Rbac權限控制
一、SpringSecurity控制授權
1.SpringSecurity授權的定義
- 不同系統權限不同:
2.SpringSecurity 通過代碼實現授權
package com.zcw.security.browser;
import com.zcw.security.core.authentication.AuthorizeConfigManager;
import com.zcw.security.core.authentication.FormAuthenticationConfig;
import com.zcw.security.core.properties.MySecurityProperties;
import com.zcw.security.core.validate.code.SmsCodeFilter;
import com.zcw.security.core.validate.code.ValidateCodeFilter;
import com.zcw.security.core.validate.code.ValidateCodeSecurityConfig;
import com.zcw.security.core.validate.code.sms.SmsCodeAuthenticationSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.social.security.SpringSocialConfigurer;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import javax.xml.ws.soap.Addressing;
/**
* @ClassName : BrowserSecurityConfig
* @Description :適配器類
* @Author : Zhaocunwei
* @Date: 2020-06-18 17:43
*/
@Component
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MySecurityProperties securityProperties;
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Autowired
private ValidateCodeSecurityConfig validateCodeSecurityConfig;
@Autowired
private SpringSocialConfigurer imoocSocialSecurityConfig;
@Autowired
private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;
@Autowired
private InvalidSessionStrategy invalidSessionStrategy;
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
@Autowired
private AuthorizeConfigManager authorizeConfigManager;
@Autowired
private FormAuthenticationConfig formAuthenticationConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
formAuthenticationConfig.configure(http);
http.apply(validateCodeSecurityConfig)
.and()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.apply(imoocSocialSecurityConfig)
.and()
//記住我配置,如果想在'記住我'登錄時記錄日誌,可以註冊一個InteractiveAuthenticationSuccessEvent事件的監聽器
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowserProperties().getRemeberMeSeconds())
.userDetailsService(userDetailsService)
.and()
.sessionManagement()
.invalidSessionStrategy(invalidSessionStrategy)
.maximumSessions(securityProperties.getBrowserProperties().getSessionProperties().getMaximumSessions())
.maxSessionsPreventsLogin(securityProperties.getBrowserProperties().getSessionProperties().isMaxSessionsPreventsLogin())
.expiredSessionStrategy(sessionInformationExpiredStrategy)
.and()
.and()
.logout()
.logoutUrl("/signOut")
.logoutSuccessHandler(logoutSuccessHandler)
.deleteCookies("JSESSIONID")
.and()
.csrf().disable();
authorizeConfigManager.config(http.authorizeRequests());
}
/**
* 記住我功能的token存取器配置
*
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
// tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
}
代碼中使用工具類把","分隔的字符串,轉換成list,表明有哪些權限
- 可以控制到方法上面,在配置類中如下實現,只能get請求
二、SpringSecurity源碼分析
- FilterSecurityInterceptor – 權限控制相關的類
控制我們如下圖,整個過濾器鏈是否可以通過的
- 匿名過濾器 AnonymousAuthenticationFilter
本類最核心的方法就是: doFilter進行邏輯判斷
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req));
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with anonymous token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
chain.doFilter(req, res);
}
也就是獲取標紅這些過濾器是否進行了身份認證。
- 核心類:
1.沒有登錄訪問被拒絕的流程
1.先進入:FilterSecurityInterceptor
- 然後我們查看一個循環方法,裏面的內容主要是URL地址的權限信息
也就是配置類中:
-根據通票器進行,是否通過請求,當投票器大於0時,直接返回
- AffirmativeBased
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.access.vote;
import java.util.*;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
/**
* Simple concrete implementation of
* {@link org.springframework.security.access.AccessDecisionManager} that grants access if
* any <code>AccessDecisionVoter</code> returns an affirmative response.
*/
public class AffirmativeBased extends AbstractAccessDecisionManager {
public AffirmativeBased(List<AccessDecisionVoter<? extends Object>> decisionVoters) {
super(decisionVoters);
}
// ~ Methods
// ========================================================================================================
/**
* This concrete implementation simply polls all configured
* {@link AccessDecisionVoter}s and grants access if any
* <code>AccessDecisionVoter</code> voted affirmatively. Denies access only if there
* was a deny vote AND no affirmative votes.
* <p>
* If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
* be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
* false).
* </p>
*
* @param authentication the caller invoking the method
* @param object the secured object
* @param configAttributes the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
}
- 最終異常一直往上拋,會拋到
2.登錄以後訪問通過的流程
- FilterSecurityInterceptor
三、權限配置
- 權限表達式
- 創建公共的模塊,對外提供接口調用
- 授權配置提供器,各個模塊和業務系統可以通過實現此接口向系統添加授權配置。
package com.zcw.security.core.authorize;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
public interface AuthorizeConfigProvider {
/**
* @param config
* @return 返回的boolean表示配置中是否有針對anyRequest的配置。在整個授權配置中,
* 應該有且僅有一個針對anyRequest的配置,如果所有的實現都沒有針對anyRequest的配置,
* 系統會自動增加一個anyRequest().authenticated()的配置。如果有多個針對anyRequest
* 的配置,則會拋出異常。
*/
boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}
package com.zcw.security.core.authorize;
import com.zcw.security.core.properties.MySecurityProperties;
import com.zcw.security.core.properties.SecurityConstants;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
/**
* @ClassName : ZcwAuthorizeConfigProvider
* @Description : 核心模塊的授權配置提供器,安全模塊涉及的url的授權配置在這裏。
* @Author : Zhaocunwei
* @Date: 2020-07-06 17:57
*/
public class ZcwAuthorizeConfigProvider implements AuthorizeConfigProvider{
@Autowired
private MySecurityProperties mySecurityProperties;
@Override
public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
config.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE,
SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_OPENID,
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",
mySecurityProperties.getBrowserProperties().getSignInPage(),
mySecurityProperties.getBrowserProperties().getSingUpUrl(),
mySecurityProperties.getBrowserProperties().getSessionProperties().getSessionInvalidUrl()).permitAll();
if (StringUtils.isNotBlank(mySecurityProperties.getBrowserProperties().getSignOutUrl())) {
config.antMatchers(mySecurityProperties.getBrowserProperties().getSignOutUrl()).permitAll();
}
return false;
}
}
package com.zcw.security.core.authorize;
import com.zcw.security.core.authentication.AuthorizeConfigManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import java.util.List;
/**
* @ClassName : ZcwAuthorizeConfigManager
* @Description : 默認的授權配置管理器
* @Author : Zhaocunwei
* @Date: 2020-07-06 18:02
*/
@Component
public class ZcwAuthorizeConfigManager implements AuthorizeConfigManager {
@Autowired
private List<AuthorizeConfigProvider> authorizeConfigProviders;
@Override
public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
boolean existAnyRequestConfig = false;
String existAnyRequestConfigName = null;
for (AuthorizeConfigProvider authorizeConfigProvider : authorizeConfigProviders) {
boolean currentIsAnyRequestConfig = authorizeConfigProvider.config(config);
if (existAnyRequestConfig && currentIsAnyRequestConfig) {
throw new RuntimeException("重複的anyRequest配置:" + existAnyRequestConfigName + ","
+ authorizeConfigProvider.getClass().getSimpleName());
} else if (currentIsAnyRequestConfig) {
existAnyRequestConfig = true;
existAnyRequestConfigName = authorizeConfigProvider.getClass().getSimpleName();
}
}
if(!existAnyRequestConfig){
config.anyRequest().authenticated();
}
}
}
package com.zcw.security;
import com.zcw.security.core.authorize.AuthorizeConfigProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
/**
* @ClassName : DemoAuthorizeConifgProvider
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 18:10
*/
public class DemoAuthorizeConifgProvider implements AuthorizeConfigProvider {
@Override
public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
//demo項目授權配置
return false;
}
}
- 控制不能訪問某個頁面:
- 把權限放到數據庫中
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>zcw-security</artifactId>
<groupId>com.zcw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zcw-security-authorize</artifactId>
<dependencies>
<dependency>
<groupId>com.zcw</groupId>
<artifactId>zcw-security-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies>
</project>
package com.zcw.security.rbac.authentication;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.repository.AdminRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
* @ClassName : RbacUserDetailsService
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:12
*/
@Component
@Transactional
public class RbacUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AdminRepository adminRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("表單登錄用戶名:" + username);
Admin admin = adminRepository.findByUsername(username);
admin.getUrls();
return admin;
}
}
package com.zcw.security.rbac.authorize;
import com.zcw.security.core.authorize.AuthorizeConfigProvider;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.stereotype.Component;
/**
* @ClassName : RbacAuthorizeConfigProvider
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:13
*/
@Component
@Order(Integer.MAX_VALUE)
public class RbacAuthorizeConfigProvider implements AuthorizeConfigProvider {
@Override
public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
config
.antMatchers(HttpMethod.GET, "/fonts/**").permitAll()
.antMatchers(HttpMethod.GET,
"/**/*.html",
"/admin/me",
"/resource").authenticated()
.anyRequest()
.access("@rbacService.hasPermission(request, authentication)");
return true;
}
}
package com.zcw.security.rbac.domain;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* @ClassName : Admin
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:14
*/
@Entity
public class Admin implements UserDetails {
/**
*
*/
private static final long serialVersionUID = -3521673552808391992L;
/**
* 數據庫主鍵
*/
@Id
@GeneratedValue
private Long id;
/**
* 審計日誌,記錄條目創建時間,自動賦值,不需要程序員手工賦值
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
/**
* 用戶名
*/
private String username;
/**
* 密碼
*/
private String password;
/**
* 用戶的所有角色
*/
@OneToMany(mappedBy = "admin", cascade = CascadeType.REMOVE)
private Set<RoleAdmin> roles = new HashSet<>();
/**
* 用戶有權訪問的所有url,不持久化到數據庫
*/
@Transient
private Set<String> urls = new HashSet<>();
/**
* 用戶有權的所有資源id,不持久化到數據庫
*/
@Transient
private Set<Long> resourceIds = new HashSet<>();
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getAuthorities(
* )
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
/**
* @return
*/
public Set<Long> getAllResourceIds() {
init(resourceIds);
forEachResource(resource -> resourceIds.add(resource.getId()));
return resourceIds;
}
/**
* @return
*/
public Long getId() {
return id;
}
/**
* @param id
*/
public void setId(Long id) {
this.id = id;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getUsername()
*/
@Override
public String getUsername() {
return username;
}
/**
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getPassword()
*/
@Override
public String getPassword() {
return password;
}
/**
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isAccountNonExpired()
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isAccountNonLocked()
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isCredentialsNonExpired()
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#isEnabled()
*/
@Override
public boolean isEnabled() {
return true;
}
/**
* @return the roles
*/
public Set<RoleAdmin> getRoles() {
return roles;
}
/**
* @param roles
* the roles to set
*/
public void setRoles(Set<RoleAdmin> roles) {
this.roles = roles;
}
/**
* @return the urls
*/
public Set<String> getUrls() {
init(urls);
forEachResource(resource -> urls.addAll(resource.getUrls()));
return urls;
}
/**
* @param data
* @param consumer
*/
private void init(Set<?> data){
if (CollectionUtils.isEmpty(data)) {
if (data == null) {
data = new HashSet<>();
}
}
}
/**
* @param consumer
*/
private void forEachResource(Consumer<Resource> consumer) {
for (RoleAdmin role : roles) {
for (RoleResource resource : role.getRole().getResources()) {
consumer.accept(resource.getResource());
}
}
}
/**
* @param urls
* the urls to set
*/
public void setUrls(Set<String> urls) {
this.urls = urls;
}
}
package com.zcw.security.rbac.domain;
import com.zcw.security.rbac.dto.ResourceInfo;
import jdk.management.resource.ResourceType;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* @ClassName : Resource
* @Description : 需要控制權限的資源,以業務人員能看懂的name呈現.實際關聯到一個或多個url上。樹形結構。
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:15
*/
@Entity
public class Resource {
/**
* 數據庫表主鍵
*/
@Id
@GeneratedValue
private Long id;
/**
* 審計日誌,記錄條目創建時間,自動賦值,不需要程序員手工賦值
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
/**
* 資源名稱,如xx菜單,xx按鈕
*/
private String name;
/**
* 資源鏈接
*/
private String link;
/**
* 圖標
*/
private String icon;
/**
* 資源類型
*/
@Enumerated(EnumType.STRING)
private ResourceType type;
/**
* 實際需要控制權限的url
*/
@ElementCollection
private Set<String> urls;
/**
* 父資源
*/
@ManyToOne
private Resource parent;
/**
* 子資源
*/
@OneToMany(mappedBy = "parent")
@OrderBy("sort ASC")
private List<Resource> childs = new ArrayList<>();
public ResourceInfo toTree(Admin admin) {
ResourceInfo result = new ResourceInfo();
BeanUtils.copyProperties(this, result);
Set<Long> resourceIds = admin.getAllResourceIds();
List<ResourceInfo> children = new ArrayList<ResourceInfo>();
for (Resource child : getChilds()) {
if(StringUtils.equals(admin.getUsername(), "admin") ||
resourceIds.contains(child.getId())){
children.add(child.toTree(admin));
}
}
result.setChildren(children);
return result;
}
public void addChild(Resource child) {
childs.add(child);
child.setParent(this);
}
/**
* 序號
*/
private int sort;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<String> getUrls() {
return urls;
}
public void setUrls(Set<String> urls) {
this.urls = urls;
}
public Resource getParent() {
return parent;
}
public void setParent(Resource parent) {
this.parent = parent;
}
public List<Resource> getChilds() {
return childs;
}
public void setChilds(List<Resource> childs) {
this.childs = childs;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
/**
* @return the link
*/
public String getLink() {
return link;
}
/**
* @param link the link to set
*/
public void setLink(String link) {
this.link = link;
}
/**
* @return the icon
*/
public String getIcon() {
return icon;
}
/**
* @param icon the icon to set
*/
public void setIcon(String icon) {
this.icon = icon;
}
/**
* @return the type
*/
public ResourceType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(ResourceType type) {
this.type = type;
}
}
package com.zcw.security.rbac.domain;
public enum ResourceType {
MENU,
BUTTON
}
package com.zcw.security.rbac.domain;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
/**
* @ClassName : Role
* @Description : 角色信息
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:16
*/
@Entity
public class Role {
/**
* 數據庫表主鍵
*/
@Id
@GeneratedValue
private Long id;
/**
* 審計日誌,記錄條目創建時間,自動賦值,不需要程序員手工賦值
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
/**
* 角色名稱
*/
@Column(length = 20, nullable = false)
private String name;
/**
* 角色擁有權限的資源集合
*/
@OneToMany(mappedBy = "role", cascade = CascadeType.REMOVE)
private Set<RoleResource> resources = new HashSet<>();
/**
* 角色的用戶集合
*/
@OneToMany(mappedBy = "role", cascade = CascadeType.REMOVE)
private Set<RoleAdmin> admins = new HashSet<>();
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the resources
*/
public Set<RoleResource> getResources() {
return resources;
}
/**
* @param resources the resources to set
*/
public void setResources(Set<RoleResource> resources) {
this.resources = resources;
}
/**
* @return the admins
*/
public Set<RoleAdmin> getAdmins() {
return admins;
}
/**
* @param admins the admins to set
*/
public void setAdmins(Set<RoleAdmin> admins) {
this.admins = admins;
}
}
package com.zcw.security.rbac.domain;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import java.util.Date;
/**
* @ClassName : RoleAdmin
* @Description : 角色用戶關係表
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:17
*/
public class RoleAdmin {
/**
* 數據庫表主鍵
*/
@Id
@GeneratedValue
private Long id;
/**
* 審計日誌,記錄條目創建時間,自動賦值,不需要程序員手工賦值
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
/**
* 角色
*/
@ManyToOne
private Role role;
/**
* 管理員
*/
@ManyToOne
private Admin admin;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the role
*/
public Role getRole() {
return role;
}
/**
* @param role the role to set
*/
public void setRole(Role role) {
this.role = role;
}
/**
* @return the admin
*/
public Admin getAdmin() {
return admin;
}
/**
* @param admin the admin to set
*/
public void setAdmin(Admin admin) {
this.admin = admin;
}
}
package com.zcw.security.rbac.domain;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import java.util.Date;
/**
* @ClassName : RoleResource
* @Description : 角色資源關係表
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:17
*/
@Entity
public class RoleResource {
/**
* 數據庫表主鍵
*/
@Id
@GeneratedValue
private Long id;
/**
* 審計日誌,記錄條目創建時間,自動賦值,不需要程序員手工賦值
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
/**
* 角色
*/
@ManyToOne
private Role role;
/**
* 資源
*/
@ManyToOne
private Resource resource;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the role
*/
public Role getRole() {
return role;
}
/**
* @param role the role to set
*/
public void setRole(Role role) {
this.role = role;
}
/**
* @return the resource
*/
public Resource getResource() {
return resource;
}
/**
* @param resource the resource to set
*/
public void setResource(Resource resource) {
this.resource = resource;
}
}
package com.zcw.security.rbac.dto;
/**
* @ClassName : AdminCondition
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:19
*/
public class AdminCondition {
private String username;
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
}
package com.zcw.security.rbac.dto;
import org.hibernate.validator.constraints.NotBlank;
/**
* @ClassName : AdminInfo
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:19
*/
public class AdminInfo {
private Long id;
/**
* 角色id
*/
@NotBlank(message = "角色id不能爲空")
private Long roleId;
/**
* 用戶名
*/
@NotBlank(message = "用戶名不能爲空")
private String username;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the roleId
*/
public Long getRoleId() {
return roleId;
}
/**
* @param roleId the roleId to set
*/
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
}
package com.zcw.security.rbac.dto;
import com.zcw.security.rbac.domain.ResourceType;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : ResourceInfo
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:20
*/
public class ResourceInfo {
/**
* 資源ID
*
* @since 1.0.0
*/
private Long id;
/**
*
*/
private Long parentId;
/**
* 資源名
*
* @since 1.0.0
*/
private String name;
/**
* 資源鏈接
*
* @since 1.0.0
*/
private String link;
/**
* 圖標
*/
private String icon;
/**
* 資源類型
*/
private ResourceType type;
/**
* 子節點
*/
private List<ResourceInfo> children = new ArrayList<>();
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the parentId
*/
public Long getParentId() {
return parentId;
}
/**
* @param parentId the parentId to set
*/
public void setParentId(Long parentId) {
this.parentId = parentId;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the link
*/
public String getLink() {
return link;
}
/**
* @param link the link to set
*/
public void setLink(String link) {
this.link = link;
}
/**
* @return the icon
*/
public String getIcon() {
return icon;
}
/**
* @param icon the icon to set
*/
public void setIcon(String icon) {
this.icon = icon;
}
/**
* @return the children
*/
public List<ResourceInfo> getChildren() {
return children;
}
/**
* @param children the children to set
*/
public void setChildren(List<ResourceInfo> children) {
this.children = children;
}
/**
* @return the type
*/
public ResourceType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(ResourceType type) {
this.type = type;
}
}
package com.zcw.security.rbac.dto;
/**
* @ClassName : RoleInfo
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:20
*/
public class RoleInfo {
private Long id;
private String name;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
package com.zcw.security.rbac.init;
/**
* @ClassName : AbstractDataInitializer
* @Description : 抽象數據初始化器,所有的數據初始化器應該繼承此類
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:20
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
public abstract class AbstractDataInitializer implements DataInitializer{
protected Logger logger = LoggerFactory.getLogger(getClass());
@Override
@Transactional
public void init() throws Exception {
if(isNeedInit()) {
logger.info("使用"+getClass().getSimpleName()+"初始化數據");
doInit();
logger.info("使用"+getClass().getSimpleName()+"初始化數據完畢");
}
}
/**
* 實際的數據初始化邏輯
* @throws IOException
*/
protected abstract void doInit() throws Exception;
/**
* 是否執行數據初始化行爲
* @return
*/
protected abstract boolean isNeedInit();
}
package com.zcw.security.rbac.init;
import com.zcw.security.rbac.ResourceRepository;
import com.zcw.security.rbac.RoleAdminRepository;
import com.zcw.security.rbac.RoleRepository;
import com.zcw.security.rbac.domain.*;
import com.zcw.security.rbac.repository.AdminRepository;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @ClassName : AdminDataInitializer
* @Description :默認的系統數據初始化器,永遠在其他數據初始化器之前執行
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:21
*/
@Component
public class AdminDataInitializer extends AbstractDataInitializer{
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RoleRepository roleRepository;
@Autowired
private AdminRepository adminRepository;
@Autowired
private RoleAdminRepository roleAdminRepository;
@Autowired
protected ResourceRepository resourceRepository;
/*
* (non-Javadoc)
*
* @see com.idea.core.spi.initializer.DataInitializer#getIndex()
*/
@Override
public Integer getIndex() {
return Integer.MIN_VALUE;
}
/*
* (non-Javadoc)
*
* @see com.idea.core.spi.initializer.AbstractDataInitializer#doInit()
*/
@Override
protected void doInit() {
initResource();
Role role = initRole();
initAdmin(role);
}
/**
* 初始化用戶數據
*
* @param customer
* @param role
* @param organ
*/
private void initAdmin(Role role) {
Admin admin = new Admin();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("123456"));
adminRepository.save(admin);
RoleAdmin roleAdmin = new RoleAdmin();
roleAdmin.setRole(role);
roleAdmin.setAdmin(admin);
roleAdminRepository.save(roleAdmin);
}
/**
* 初始化角色數據
*
* @param customer
* @return
*/
private Role initRole() {
Role role = new Role();
role.setName("超級管理員");
roleRepository.save(role);
return role;
}
/**
* 初始化菜單數據
*/
protected void initResource() {
Resource root = createRoot("根節點");
createResource("首頁", "", "home", root);
Resource menu1 = createResource("平臺管理", "", "desktop", root);
// createResource("資源管理", "resource", "", menu1);
createResource("角色管理", "role", "", menu1);
createResource("管理員管理", "admin", "", menu1);
}
/*
* (non-Javadoc)
*
* @see com.idea.core.spi.initializer.AbstractDataInitializer#isNeedInit()
*/
@Override
protected boolean isNeedInit() {
return adminRepository.count() == 0;
}
/**
* @param id
* @param name
* @param link
* @param iconName
* @param parent
* @return
*/
protected Resource createRoot(String name) {
Resource node = new Resource();
node.setName(name);
resourceRepository.save(node);
return node;
}
/**
* @param id
* @param name
* @param parent
* @return
*/
protected Resource createResource(String name, Resource parent) {
return createResource(name, null, null, parent);
}
/**
* @param id
* @param name
* @param link
* @param iconName
* @param parent
* @return
*/
protected Resource createResource(String name, String link, String iconName, Resource parent) {
Resource node = new Resource();
node.setName(name);
node.setIcon(iconName);
node.setParent(parent);
node.setType(ResourceType.MENU);
if (StringUtils.isNotBlank(link)) {
node.setLink(link + "Manage");
Set<String> urls = new HashSet<>();
urls.add(link + "Manage");
urls.add("/" + link + "/**");
node.setUrls(urls);
}
resourceRepository.save(node);
return node;
}
}
package com.zcw.security.rbac.init;
/**
* 數據初始化器,設計此接口的目的是封裝系統數據的初始化行爲,
* 開發人員可以向系統中添加此接口的實現,來增加自定義的數據初始化行爲.
*/
public interface DataInitializer {
/**
* 初始化器的執行順序,數值越大的初始化器越靠後執行
*
* @return
*/
Integer getIndex();
/**
* 初始化數據的方法
* @throws Exception
*/
void init() throws Exception;
}
package com.zcw.security.rbac.init;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import java.util.List;
/**
* @ClassName : SystemDataInitializer
* @Description :系統初始化器
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:23
*/
public class SystemDataInitializer implements ApplicationListener<ContextRefreshedEvent> {
/**
* 系統中所有的{@link DataInitializer}接口實現
*/
@Autowired(required = false)
private List<DataInitializer> dataInitializers;
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 循環調用系統中所有的{@link DataInitializer}
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(CollectionUtils.isNotEmpty(dataInitializers)){
dataInitializers.sort((initor1, initor2) -> {
return initor1.getIndex().compareTo(initor2.getIndex());
});
dataInitializers.stream().forEach(dataInitializer -> {
try {
dataInitializer.init();
} catch (Exception e) {
logger.info("系統數據初始化失敗("+dataInitializer.getClass().getSimpleName()+")", e);
}
});
}
}
}
package com.zcw.security.rbac.repository.spec;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.dto.AdminCondition;
import com.zcw.security.rbac.repository.support.QueryWraper;
import com.zcw.security.rbac.repository.support.ZcwSpecification;
/**
* @ClassName : AdminSpec
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:24
*/
public class AdminSpec extends ZcwSpecification<Admin, AdminCondition> {
public AdminSpec(AdminCondition condition) {
super(condition);
}
@Override
protected void addCondition(QueryWraper<Admin> queryWraper) {
addLikeCondition(queryWraper, "username");
}
}
package com.zcw.security.rbac.repository.support;
import java.util.Collection;
import java.util.Date;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
/**
* @ClassName : AbstractConditionBuilder
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:25
*/
public abstract class AbstractConditionBuilder <T>{
/**
* 添加in條件
*
* @param queryWraper
* @param values
*/
protected void addInConditionToColumn(QueryWraper<T> queryWraper, String column, Object values) {
if (needAddCondition(values)) {
Path<?> fieldPath = getPath(queryWraper.getRoot(), column);
if(values.getClass().isArray()) {
queryWraper.addPredicate(fieldPath.in((Object[])values));
}else if(values instanceof Collection) {
queryWraper.addPredicate(fieldPath.in((Collection<?>)values));
}
}
}
/**
* 添加between條件查詢
* @param queryWraper
* @param experssion
* @param minValue 範圍下限
* @param maxValue 範圍上限
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void addBetweenConditionToColumn(QueryWraper<T> queryWraper, String column, Comparable minValue, Comparable maxValue) {
if (minValue != null || maxValue != null) {
Path<? extends Comparable> fieldPath = getPath(queryWraper.getRoot(), column);
if(minValue != null && maxValue != null){
queryWraper.addPredicate(queryWraper.getCb().between(fieldPath, minValue, processMaxValueOnDate(maxValue)));
}else if(minValue != null){
queryWraper.addPredicate(queryWraper.getCb().greaterThanOrEqualTo(fieldPath, minValue));
}else if(maxValue != null){
queryWraper.addPredicate(queryWraper.getCb().lessThanOrEqualTo(fieldPath, processMaxValueOnDate(maxValue)));
}
}
}
/**
* 當範圍查詢的條件是小於,並且值的類型是Date時,將傳入的Date值變爲當天的夜裏12點的值。
* @param maxValue
* @return
* @author zhailiang
* @since 2016年12月14日
*/
@SuppressWarnings("rawtypes")
private Comparable processMaxValueOnDate(Comparable maxValue) {
if(maxValue instanceof Date) {
maxValue = new DateTime(maxValue).withTimeAtStartOfDay().plusDays(1).plusSeconds(-1).toDate();
}
return maxValue;
}
/**
* 添加大於條件查詢
* @param queryWraper
* @param experssion
* @param minValue
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void addGreaterThanConditionToColumn(QueryWraper<T> queryWraper, String column, Comparable minValue) {
if (minValue != null) {
Path<? extends Comparable> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().greaterThan(fieldPath, minValue));
}
}
/**
* 添加大於等於條件查詢
* @param queryWraper
* @param experssion
* @param minValue
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void addGreaterThanOrEqualConditionToColumn(QueryWraper<T> queryWraper, String column, Comparable minValue) {
if (minValue != null) {
Path<? extends Comparable> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().greaterThanOrEqualTo(fieldPath, minValue));
}
}
/**
* 添加小於條件查詢
* @param queryWraper
* @param experssion
* @param maxValue
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void addLessThanConditionToColumn(QueryWraper<T> queryWraper, String column, Comparable maxValue) {
if (maxValue != null) {
Path<? extends Comparable> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().lessThan(fieldPath, processMaxValueOnDate(maxValue)));
}
}
/**
* 添加小於等於條件查詢
* @param queryWraper
* @param experssion
* @param maxValue
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void addLessThanOrEqualConditionToColumn(QueryWraper<T> queryWraper, String column, Comparable maxValue) {
if (maxValue != null) {
Path<? extends Comparable> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().lessThanOrEqualTo(fieldPath, processMaxValueOnDate(maxValue)));
}
}
/**
* <pre>
* 添加like條件
* <pre>
* @param queryWraper
* @param column
* @param value
* @author jojo 2014-8-12 下午3:13:44
*/
protected void addLikeConditionToColumn(QueryWraper<T> queryWraper, String column, String value) {
if (StringUtils.isNotBlank(value)) {
queryWraper.addPredicate(createLikeCondition(queryWraper, column, value));
}
}
/**
* @param queryWraper
* @param column
* @param value
* @return
* @author zhailiang
* @since 2016年12月13日
*/
@SuppressWarnings("unchecked")
protected Predicate createLikeCondition(QueryWraper<T> queryWraper, String column, String value) {
Path<String> fieldPath = getPath(queryWraper.getRoot(), column);
Predicate condition = queryWraper.getCb().like(fieldPath, "%" + value + "%");
return condition;
}
/**
* <pre>
* 添加like條件
* <pre>
* @param queryWraper
* @param column
* @param value
* @author jojo 2014-8-12 下午3:13:44
*/
@SuppressWarnings("unchecked")
protected void addStartsWidthConditionToColumn(QueryWraper<T> queryWraper, String column, String value) {
if (StringUtils.isNotBlank(value)) {
Path<String> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().like(fieldPath, value + "%"));
}
}
/**
* 添加等於條件
* @param queryWraper
* @param column 指出要向哪個字段添加條件
* @param value 指定字段的值
*/
protected void addEqualsConditionToColumn(QueryWraper<T> queryWraper, String column, Object value) {
if(needAddCondition(value)) {
Path<?> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().equal(fieldPath, value));
}
}
/**
* 添加不等於條件
* @param queryWraper
* @param column 指出要向哪個字段添加條件
* @param value 指定字段的值
*/
protected void addNotEqualsConditionToColumn(QueryWraper<T> queryWraper, String column, Object value) {
if(needAddCondition(value)) {
Path<?> fieldPath = getPath(queryWraper.getRoot(), column);
queryWraper.addPredicate(queryWraper.getCb().notEqual(fieldPath, value));
}
}
/**
* <pre>
*
* <pre>
* @param root
* @param property
* @return
* @author jojo 2014-8-12 下午3:06:58
*/
@SuppressWarnings("rawtypes")
protected Path getPath(Root root, String property){
String[] names = StringUtils.split(property, ".");
Path path = root.get(names[0]);
for (int i = 1; i < names.length; i++) {
path = path.get(names[i]);
}
return path;
}
/**
* <pre>
* 判斷是否需要添加where條件
* <pre>
* @param value
* @return
* @author jojo 2014-8-12 下午3:07:00
*/
@SuppressWarnings("rawtypes")
protected boolean needAddCondition(Object value) {
boolean addCondition = false;
if (value != null) {
if(value instanceof String) {
if(StringUtils.isNotBlank(value.toString())) {
addCondition = true;
}
}else if(value.getClass().isArray()) {
if(ArrayUtils.isNotEmpty((Object[]) value)) {
addCondition = true;
}
}else if(value instanceof Collection) {
if(CollectionUtils.isNotEmpty((Collection) value)) {
addCondition = true;
}
}else {
addCondition = true;
}
}
return addCondition;
}
}
package com.zcw.security.rbac.repository.support;
import org.springframework.beans.BeanUtils;
/**
* @ClassName : AbstractDomain2InfoConverter
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:26
*/
public abstract class AbstractDomain2InfoConverter <T, I> implements Domain2InfoConverter<T, I>{
@SuppressWarnings("unchecked")
@Override
public I convert(T domain) {
I info = null;
try {
Class<I> clazz = GenericUtils.getGenericClass(getClass(), 1);
info = clazz.newInstance();
BeanUtils.copyProperties(domain, info);
doConvert(domain, info);
} catch (Exception e) {
e.printStackTrace();
}
return info;
}
protected abstract void doConvert(T domain, I info) throws Exception;
}
package com.zcw.security.rbac.repository.support;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
/**
* @ClassName : AbstractEventConditionBuilder
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:27
*/
public abstract class AbstractEventConditionBuilder <T, C> extends AbstractConditionBuilder<T> {
/**
* 查詢條件
*/
private C condition;
/**
* @param condition 查詢條件
*/
public AbstractEventConditionBuilder(C condition){
this.condition = condition;
}
/**
* 向查詢中添加包含(like)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取,並且指出要向哪個字段添加包含(like)條件。(同一個字段名稱)
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addLikeCondition(QueryWraper<T> queryWraper, String field){
addLikeCondition(queryWraper, field, field);
}
/**
* 向查詢中添加包含(like)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取
* @param column 指出要向哪個字段添加包含(like)條件
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addLikeCondition(QueryWraper<T> queryWraper, String field, String column){
addLikeConditionToColumn(queryWraper, column, (String)
getValue(getCondition(), field));
}
/**
* 向查詢中添加包含(like)條件,%放在值後面
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取,並且指出要向哪個字段添加包含(like)條件。(同一個字段名稱)
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addStartsWidthCondition(QueryWraper<T> queryWraper, String field){
addStartsWidthCondition(queryWraper, field, field);
}
/**
* 向查詢中添加包含(like)條件,%放在值後面
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取
* @param column 指出要向哪個字段添加包含(like)條件
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addStartsWidthCondition(QueryWraper<T> queryWraper, String field, String column){
addStartsWidthConditionToColumn(queryWraper, column, (String)
getValue(getCondition(), field));
}
/**
* 向查詢中添加等於(=)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取,並且指出要向哪個字段添加條件。(同一個字段名稱)
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addEqualsCondition(QueryWraper<T> queryWraper, String field){
addEqualsCondition(queryWraper, field, field);
}
/**
* 向查詢中添加等於(=)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取
* @param column 指出要向哪個字段添加條件
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addEqualsCondition(QueryWraper<T> queryWraper, String field, String column){
addEqualsConditionToColumn(queryWraper, column,
getValue(getCondition(), field));
}
/**
* 向查詢中添加不等於(!=)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取,並且指出要向哪個字段添加條件。(同一個字段名稱)
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addNotEqualsCondition(QueryWraper<T> queryWraper, String field){
addNotEqualsCondition(queryWraper, field, field);
}
/**
* 向查詢中添加等於(=)條件
*
* @param queryWraper
* @param field 指出查詢條件的值從condition對象的哪個字段裏取
* @param column 指出要向哪個字段添加條件
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
protected void addNotEqualsCondition(QueryWraper<T> queryWraper, String field, String column){
addNotEqualsConditionToColumn(queryWraper, column, getValue(getCondition(), field));
}
/**
* <pre>
* 向查詢中添加in條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addInCondition(QueryWraper<T> queryWraper, String field) {
addInCondition(queryWraper, field, field);
}
/**
* <pre>
* 向查詢中添加in條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
protected void addInCondition(QueryWraper<T> queryWraper, String field, String column) {
addInConditionToColumn(queryWraper, column,
getValue(getCondition(), field));
}
/**
* <pre>
* 向查詢中添加between條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addBetweenCondition(QueryWraper<T> queryWraper, String field) {
addBetweenCondition(queryWraper, field, field+"To", field);
}
/**
* <pre>
* 向查詢中添加between條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
@SuppressWarnings("rawtypes")
protected void addBetweenCondition(QueryWraper<T> queryWraper, String startField, String endField, String column) {
addBetweenConditionToColumn(queryWraper, column,
(Comparable)getValue(getCondition(), startField),
(Comparable)getValue(getCondition(), endField));
}
/**
* <pre>
* 向查詢中添加大於條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addGreaterThanCondition(QueryWraper<T> queryWraper, String field) {
addGreaterThanCondition(queryWraper, field, field);
}
/**
* <pre>
* 向查詢中添加大於條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
@SuppressWarnings("rawtypes")
protected void addGreaterThanCondition(QueryWraper<T> queryWraper, String field, String column) {
addGreaterThanConditionToColumn(queryWraper, column,
(Comparable)getValue(getCondition(), field));
}
/**
* <pre>
* 向查詢中添加大於等於條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addGreaterThanOrEqualCondition(QueryWraper<T> queryWraper, String field) {
addGreaterThanOrEqualCondition(queryWraper, field, field);
}
/**
* <pre>
* 向查詢中添加大於等於條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
@SuppressWarnings("rawtypes")
protected void addGreaterThanOrEqualCondition(QueryWraper<T> queryWraper, String field, String column) {
addGreaterThanOrEqualConditionToColumn(queryWraper, column,
(Comparable)getValue(getCondition(), field));
}
/**
* <pre>
* 向查詢中添加小於條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addLessThanCondition(QueryWraper<T> queryWraper, String field) {
addLessThanCondition(queryWraper, field, field);
}
/**
* <pre>
* 向查詢中添加小於條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
@SuppressWarnings("rawtypes")
protected void addLessThanCondition(QueryWraper<T> queryWraper, String field, String column) {
addLessThanConditionToColumn(queryWraper, column,
(Comparable)getValue(getCondition(), field));
}
/**
* <pre>
* 向查詢中添加小於等於條件
* <pre>
* @param queryWraper
* @param field
* @author jojo 2014-8-12 下午3:26:50
*/
protected void addLessThanOrEqualCondition(QueryWraper<T> queryWraper, String field) {
addLessThanOrEqualCondition(queryWraper, field, field);
}
/**
* <pre>
* 向查詢中添加小於等於條件
* <pre>
* @param queryWraper
* @param field
* @param column
* @author jojo 2014-8-12 下午3:27:46
*/
@SuppressWarnings("rawtypes")
protected void addLessThanOrEqualCondition(QueryWraper<T> queryWraper, String field, String column) {
addLessThanOrEqualConditionToColumn(queryWraper, column,
(Comparable)getValue(getCondition(), field));
}
private Object getValue(C condition, String field) {
try {
return PropertyUtils.getProperty(condition, field);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
/**
* @return the condition
*/
public C getCondition() {
return condition;
}
/**
* @param condition the condition to set
*/
public void setCondition(C condition) {
this.condition = condition;
}
}
package com.zcw.security.rbac.repository.support;
import org.springframework.core.convert.converter.Converter;
/**
* @ClassName : Domain2InfoConverter
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:28
*/
public interface Domain2InfoConverter<T, I> extends Converter<T, I> {
}
package com.zcw.security.rbac.repository.support;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* @ClassName : GenericUtils
* @Description :泛型工具
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:28
*/
public class GenericUtils {
/**
* 獲取目標class的第一個泛型參數的類型
* @param clazz
* @return
*/
@SuppressWarnings("rawtypes")
public static Class getGenericClass(Class clazz){
return getGenericClass(clazz, 0);
}
/**
* 獲取目標class的指定位置的泛型參數的類型
* @param clazz
* @param index 泛型參數的位置,第一個參數爲0
* @return
*/
@SuppressWarnings("rawtypes")
public static Class getGenericClass(Class clazz, int index) {
Type t = clazz.getGenericSuperclass();
if(t instanceof ParameterizedType){
Type[] params = ((ParameterizedType)t).getActualTypeArguments();
if(params[index] instanceof ParameterizedType){
return ((ParameterizedType)params[index]).getRawType().getClass();
}else{
return (Class)params[index];
}
}
throw new RuntimeException("無法獲得泛型的類型");
}
}
package com.zcw.security.rbac.repository.support;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : QueryResultConverter
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:31
*/
public class QueryResultConverter {
private static Logger logger = LoggerFactory.getLogger(QueryResultConverter.class);
/**
* @param pageData
* @param clazz
* @param pageable
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static <T, I> Page<I> convert(Page<T> pageData, Class<I> clazz, Pageable pageable) {
List<T> contents = pageData.getContent();
List<I> infos = convert(contents, clazz);
return new PageImpl<I>(infos, pageable, pageData.getTotalElements());
}
public static <I, T> List<I> convert(List<T> contents, Class<I> clazz) {
List<I> infos = new ArrayList<I>();
for (T domain : contents) {
I info = null;
try {
info = clazz.newInstance();
BeanUtils.copyProperties(info, domain);
} catch (Exception e) {
logger.info("轉換數據失敗", e);
throw new RuntimeException("轉換數據失敗");
}
if(info != null) {
infos.add(info);
}
}
return infos;
}
/**
* @param pageData
* @param clazz
* @param pageable
* @param converter
* @return
*/
public static <T, I> Page<I> convert(Page<T> pageData, Pageable pageable, Domain2InfoConverter<T, I> converter) {
List<T> contents = pageData.getContent();
List<I> infos = convert(contents, converter);
return new PageImpl<I>(infos, pageable, pageData.getTotalElements());
}
public static <I, T> List<I> convert(List<T> contents, Domain2InfoConverter<T, I> converter) {
List<I> infos = new ArrayList<I>();
for (T domain : contents) {
infos.add(converter.convert(domain));
}
return infos;
}
}
package com.zcw.security.rbac.repository.support;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;
/**
* @ClassName : QueryWraper
* @Description : 包裝用於構建JPA動態查詢時所需的對象
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:31
*/
public class QueryWraper<T> {
/**
* @param root
* JPA Root
* @param cb
* JPA CriteriaBuilder
* @param predicates
* JPA Predicate 集合
*/
public QueryWraper(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb, List<Predicate> predicates) {
this.root = root;
this.query = query;
this.cb = cb;
this.predicates = predicates;
}
/**
* JPA Root
*/
private Root<T> root;
/**
* JPA CriteriaBuilder
*/
private CriteriaBuilder cb;
/**
* JPA Predicate 集合
*/
private List<Predicate> predicates;
/**
* JPA 查詢對象
*/
private CriteriaQuery<?> query;
/**
* <pre>
*
* <pre>
* @param predicate
* @author jojo 2014-8-12 下午3:12:36
*/
public void addPredicate(Predicate predicate) {
this.predicates.add(predicate);
}
/**
* @return the root
*/
public Root<T> getRoot() {
return root;
}
/**
* @param root
* the root to set
*/
public void setRoot(Root<T> root) {
this.root = root;
}
/**
* @return the cb
*/
public CriteriaBuilder getCb() {
return cb;
}
/**
* @param cb
* the cb to set
*/
public void setCb(CriteriaBuilder cb) {
this.cb = cb;
}
/**
* @return the predicates
*/
public List<Predicate> getPredicates() {
return predicates;
}
/**
* @param predicates
* the predicates to set
*/
public void setPredicates(List<Predicate> predicates) {
this.predicates = predicates;
}
/**
* @return the query
*/
public CriteriaQuery<?> getQuery() {
return query;
}
/**
* @param query the query to set
*/
public void setQuery(CriteriaQuery<?> query) {
this.query = query;
}
}
package com.zcw.security.rbac.repository.support;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.boot.spi.MetadataBuildingContext;
/**
* @ClassName : ZcwImplicitNamingStrategy
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:28
*/
public class ZcwImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {
private static final long serialVersionUID = 769122522217805485L;
@Override
protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) {
return super.toIdentifier("imooc_"+stringForm.toLowerCase(), buildingContext);
}
}
package com.zcw.security.rbac.repository.support;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : ZcwSpecification
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:29
*/
public abstract class ZcwSpecification<T, C> extends AbstractEventConditionBuilder<T, C>
implements Specification<T> {
/**
* @param condition
*/
public ZcwSpecification(C condition) {
super(condition);
}
/**
*
* 構建查詢條件,子類必須實現addCondition方法來編寫查詢的邏輯。
*
* 子類可以通過addFetch方法控制查詢的關聯和抓取行爲。
*
*/
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if (Long.class != query.getResultType()) {
addFetch(root);
}
List<Predicate> predicates = new ArrayList<Predicate>();
QueryWraper<T> queryWraper = new QueryWraper<T>(root, query, cb, predicates);
addCondition(queryWraper);
Predicate permissionCondition = getPermissionCondition(queryWraper);
if (permissionCondition != null) {
queryWraper.addPredicate(permissionCondition);
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
/**
* 添加權限條件,如果要查詢的domain實現了{@link ManagedByOrgan}接口,那麼傳入的Condition對象也應該實現
* {@link ManagedByOrgan}接口,
* 程序會嘗試從Condition對象獲取organFullId,然後作爲like查詢條件添加到查詢中。
* 查出所有以傳入的organFullId開頭的domain.
*
* @param queryWraper
* @return
*/
protected Predicate getPermissionCondition(QueryWraper<T> queryWraper) {
return null;
}
/**
* <pre>
* 子類可以通過覆蓋此方法實現關聯抓取,避免n+1查詢
*
* <pre>
*
* @param root
* @author jojo 2014-7-22 上午9:49:26
*/
protected void addFetch(Root<T> root) {
}
protected abstract void addCondition(QueryWraper<T> queryWraper);
}
package com.zcw.security.rbac.repository;
import com.zcw.security.rbac.ZcwRepository;
import com.zcw.security.rbac.domain.Admin;
import org.springframework.stereotype.Repository;
@Repository
public interface AdminRepository extends ZcwRepository<Admin> {
Admin findByUsername(String username);
}
package com.zcw.security.rbac.service.impl;
import com.zcw.security.rbac.RoleAdminRepository;
import com.zcw.security.rbac.RoleRepository;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.domain.RoleAdmin;
import com.zcw.security.rbac.dto.AdminCondition;
import com.zcw.security.rbac.dto.AdminInfo;
import com.zcw.security.rbac.repository.AdminRepository;
import com.zcw.security.rbac.repository.spec.AdminSpec;
import com.zcw.security.rbac.repository.support.QueryResultConverter;
import com.zcw.security.rbac.service.AdminService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @ClassName : AdminServiceImpl
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:39
*/
@Service
@Transactional
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminRepository adminRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private RoleAdminRepository roleAdminRepository;
@Autowired
private PasswordEncoder passwordEncoder;
/* (non-Javadoc)
*/
@Override
public AdminInfo create(AdminInfo adminInfo) {
Admin admin = new Admin();
BeanUtils.copyProperties(adminInfo, admin);
admin.setPassword(passwordEncoder.encode("123456"));
adminRepository.save(admin);
adminInfo.setId(admin.getId());
createRoleAdmin(adminInfo, admin);
return adminInfo;
}
/* (non-Javadoc)
*/
@Override
public AdminInfo update(AdminInfo adminInfo) {
Admin admin = adminRepository.findOne(adminInfo.getId());
BeanUtils.copyProperties(adminInfo, admin);
createRoleAdmin(adminInfo, admin);
return adminInfo;
}
/**
* 創建角色用戶關係數據。
* @param adminInfo
* @param admin
*/
private void createRoleAdmin(AdminInfo adminInfo, Admin admin) {
if(CollectionUtils.isNotEmpty(admin.getRoles())){
roleAdminRepository.delete(admin.getRoles());
}
RoleAdmin roleAdmin = new RoleAdmin();
roleAdmin.setRole(roleRepository.getOne(adminInfo.getRoleId()));
roleAdmin.setAdmin(admin);
roleAdminRepository.save(roleAdmin);
}
/* (non-Javadoc)
*/
@Override
public void delete(Long id) {
adminRepository.delete(id);
}
/* (non-Javadoc)
*/
@Override
public AdminInfo getInfo(Long id) {
Admin admin = adminRepository.findOne(id);
AdminInfo info = new AdminInfo();
BeanUtils.copyProperties(admin, info);
return info;
}
/* (non-Javadoc)
*/
@Override
public Page<AdminInfo> query(AdminCondition condition, Pageable pageable) {
Page<Admin> admins = adminRepository.findAll(new AdminSpec(condition), pageable);
return QueryResultConverter.convert(admins, AdminInfo.class, pageable);
}
}
package com.zcw.security.rbac.service.impl;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.service.RbacService;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.Set;
/**
* @ClassName : RbacServiceImpl
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:40
*/
@Component("rbacService")
public class RbacServiceImpl implements RbacService {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
boolean hasPermission = false;
if (principal instanceof Admin) {
//如果用戶名是admin,就永遠返回true
if (StringUtils.equals(((Admin) principal).getUsername(), "admin")) {
hasPermission = true;
} else {
// 讀取用戶所擁有權限的所有URL
Set<String> urls = ((Admin) principal).getUrls();
for (String url : urls) {
if (antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
}
}
return hasPermission;
}
}
package com.zcw.security.rbac.service.impl;
import com.zcw.security.rbac.ResourceRepository;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.domain.Resource;
import com.zcw.security.rbac.dto.ResourceInfo;
import com.zcw.security.rbac.repository.AdminRepository;
import com.zcw.security.rbac.service.ResourceService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @ClassName : ResourceServiceImpl
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:41
*/
@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {
@Autowired
private ResourceRepository resourceRepository;
@Autowired
private AdminRepository adminRepository;
/* (non-Javadoc)
*/
@Override
public ResourceInfo getTree(Long adminId) {
Admin admin = adminRepository.findOne(adminId);
return resourceRepository.findByName("根節點").toTree(admin);
}
/* (non-Javadoc)
* @see com.imooc.security.rbac.service.ResourceService#getInfo(java.lang.Long)
*/
@Override
public ResourceInfo getInfo(Long id) {
Resource resource = resourceRepository.findOne(id);
ResourceInfo resourceInfo = new ResourceInfo();
BeanUtils.copyProperties(resource, resourceInfo);
return resourceInfo;
}
@Override
public ResourceInfo create(ResourceInfo info) {
Resource parent = resourceRepository.findOne(info.getParentId());
if(parent == null){
parent = resourceRepository.findByName("根節點");
}
Resource resource = new Resource();
BeanUtils.copyProperties(info, resource);
parent.addChild(resource);
info.setId(resourceRepository.save(resource).getId());
return info;
}
@Override
public ResourceInfo update(ResourceInfo info) {
Resource resource = resourceRepository.findOne(info.getId());
BeanUtils.copyProperties(info, resource);
return info;
}
@Override
public void delete(Long id) {
resourceRepository.delete(id);
}
/* (non-Javadoc)
* @see com.imooc.security.rbac.service.ResourceService#move(java.lang.Long, boolean)
*/
@Override
public Long move(Long id, boolean up) {
Resource resource = resourceRepository.findOne(id);
int index = resource.getSort();
List<Resource> childs = resource.getParent().getChilds();
for (int i = 0; i < childs.size(); i++) {
Resource current = childs.get(i);
if(current.getId().equals(id)) {
if(up){
if(i != 0) {
Resource pre = childs.get(i - 1);
resource.setSort(pre.getSort());
pre.setSort(index);
resourceRepository.save(pre);
}
}else{
if(i != childs.size()-1) {
Resource next = childs.get(i + 1);
resource.setSort(next.getSort());
next.setSort(index);
resourceRepository.save(next);
}
}
}
}
resourceRepository.save(resource);
return resource.getParent().getId();
}
}
package com.zcw.security.rbac.service.impl;
import com.zcw.security.rbac.ResourceRepository;
import com.zcw.security.rbac.RoleRepository;
import com.zcw.security.rbac.RoleResourceRepository;
import com.zcw.security.rbac.domain.Role;
import com.zcw.security.rbac.domain.RoleResource;
import com.zcw.security.rbac.dto.RoleInfo;
import com.zcw.security.rbac.repository.support.QueryResultConverter;
import com.zcw.security.rbac.service.RoleService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @ClassName : RoleServiceImpl
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:43
*/
@Service
@Transactional
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleRepository roleRepository;
@Autowired
private ResourceRepository resourceRepository;
@Autowired
private RoleResourceRepository roleResourceRepository;
/* (non-Javadoc)
*/
@Override
public RoleInfo create(RoleInfo info) {
Role role = new Role();
BeanUtils.copyProperties(info, role);
info.setId(roleRepository.save(role).getId());
return info;
}
/* (non-Javadoc)
*/
@Override
public RoleInfo update(RoleInfo info) {
Role role = roleRepository.findOne(info.getId());
BeanUtils.copyProperties(info, role);
return info;
}
/**
* (non-Javadoc)
*/
@Override
public void delete(Long id) {
Role role = roleRepository.findOne(id);
if(CollectionUtils.isNotEmpty(role.getAdmins())){
throw new RuntimeException("不能刪除有下掛用戶的角色");
}
roleRepository.delete(id);
}
//
// @Override
// public String[] getRoleMenus(Long id) {
// return StringUtils.split(roleRepository.findOne(id).getMenus(), ",");
// }
//
// /**
// * (non-Javadoc)
// */
// @Override
// public void setRoleMenu(Long roleId, String menuIds) {
// Role role = roleRepository.findOne(roleId);
// role.setMenus(menuIds);
// }
/**
* (non-Javadoc)
*/
@Override
public RoleInfo getInfo(Long id) {
Role role = roleRepository.findOne(id);
RoleInfo info = new RoleInfo();
BeanUtils.copyProperties(role, info);
return info;
}
/* (non-Javadoc)
*/
@Override
public List<RoleInfo> findAll() {
return QueryResultConverter.convert(roleRepository.findAll(), RoleInfo.class);
}
@Override
public String[] getRoleResources(Long id) {
Role role = roleRepository.findOne(id);
Set<String> resourceIds = new HashSet<>();
for (RoleResource resource : role.getResources()) {
resourceIds.add(resource.getResource().getId().toString());
}
return resourceIds.toArray(new String[resourceIds.size()]);
}
/**
* (non-Javadoc)
*/
@Override
public void setRoleResources(Long roleId, String resourceIds) {
resourceIds = StringUtils.removeEnd(resourceIds, ",");
Role role = roleRepository.findOne(roleId);
roleResourceRepository.delete(role.getResources());
String[] resourceIdArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(resourceIds, ",");
for (String resourceId : resourceIdArray) {
RoleResource roleResource = new RoleResource();
roleResource.setRole(role);
roleResource.setResource(resourceRepository.getOne(new Long(resourceId)));
roleResourceRepository.save(roleResource);
}
}
}
package com.zcw.security.rbac.service;
import com.zcw.security.rbac.dto.AdminCondition;
import com.zcw.security.rbac.dto.AdminInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
/**
* 管理員服務
*/
public interface AdminService {
/**
* 創建管理員
* @param adminInfo
* @return
*/
AdminInfo create(AdminInfo adminInfo);
/**
* 修改管理員
* @param adminInfo
* @return
*/
AdminInfo update(AdminInfo adminInfo);
/**
* 刪除管理員
* @param id
*/
void delete(Long id);
/**
* 獲取管理員詳細信息
* @param id
* @return
*/
AdminInfo getInfo(Long id);
/**
* 分頁查詢管理員
* @param condition
* @return
*/
Page<AdminInfo> query(AdminCondition condition, Pageable pageable);
}
package com.zcw.security.rbac.service;
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
public interface RbacService {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
package com.zcw.security.rbac.service;
import com.zcw.security.rbac.dto.ResourceInfo;
/**
* 資源服務
*/
public interface ResourceService {
/**
* 獲取資源樹
*
* @param userId 用戶ID
* @since 1.0.0
*/
ResourceInfo getTree(Long userId);
/**
* 根據資源ID獲取資源信息
*
* @param id 資源ID
* @return ResourceInfo 資源信息
* @date 2015年7月10日下午7:01:48
* @since 1.0.0
*/
ResourceInfo getInfo(Long id);
/**
* 新增資源
*
* @param info 頁面傳入的資源信息
* @return ResourceInfo 資源信息
* @date 2015年7月10日下午7:01:51
* @since 1.0.0
*/
ResourceInfo create(ResourceInfo info);
/**
* 更新資源
*
* @param info 頁面傳入的資源信息
* @return ResourceInfo 資源信息
* @since 1.0.0
*/
ResourceInfo update(ResourceInfo info);
/**
* 根據指定ID刪除資源信息
*
* @param id 資源ID
* @since 1.0.0
*/
void delete(Long id);
/**
* 上移/下移資源
* @param id
* @param up
*/
Long move(Long id, boolean up);
}
package com.zcw.security.rbac.service;
import com.zcw.security.rbac.dto.RoleInfo;
import java.util.List;
/**
* 角色服務
*/
public interface RoleService {
/**
* 創建角色
* @param roleInfo
* @return
*/
RoleInfo create(RoleInfo roleInfo);
/**
* 修改角色
* @param roleInfo
* @return
*/
RoleInfo update(RoleInfo roleInfo);
/**
* 刪除角色
* @param id
*/
void delete(Long id);
/**
* 獲取角色詳細信息
* @param id
* @return
*/
RoleInfo getInfo(Long id);
/**
* 查詢所有角色
* @param condition
* @return
*/
List<RoleInfo> findAll();
/**
* @param id
* @return
*/
String[] getRoleResources(Long id);
/**
* @param id
* @param ids
*/
void setRoleResources(Long id, String ids);
}
package com.zcw.security.rbac.web.controller;
import com.zcw.security.rbac.dto.AdminCondition;
import com.zcw.security.rbac.dto.AdminInfo;
import com.zcw.security.rbac.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName : AdminController
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:45
*/
@RestController
@RequestMapping("/admin")
public class AdminController {
@Autowired
private AdminService adminService;
/**
* 獲取當前登錄的管理員信息
* @param adminInfo
* @return
*/
@GetMapping("/me")
public AdminInfo me(@AuthenticationPrincipal UserDetails user) {
AdminInfo info = new AdminInfo();
info.setUsername(user.getUsername());
return info;
}
/**
* 創建管理員
* @param adminInfo
* @return
*/
@PostMapping
public AdminInfo create(@RequestBody AdminInfo adminInfo) {
return adminService.create(adminInfo);
}
/**
* 修改管理員信息
* @param adminInfo
* @return
*/
@PutMapping("/{id}")
public AdminInfo update(@RequestBody AdminInfo adminInfo) {
return adminService.update(adminInfo);
}
/**
* 刪除管理員
* @param id
*/
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
adminService.delete(id);
}
/**
* 獲取管理員詳情
* @param id
* @return
*/
@GetMapping("/{id}")
public AdminInfo getInfo(@PathVariable Long id) {
return adminService.getInfo(id);
}
/**
* 分頁查詢管理員
* @param adminInfo
* @param pageable
* @return
*/
@GetMapping
public Page<AdminInfo> query(AdminCondition condition, Pageable pageable) {
return adminService.query(condition, pageable);
}
}
package com.zcw.security.rbac.web.controller;
import com.zcw.security.rbac.domain.Admin;
import com.zcw.security.rbac.dto.ResourceInfo;
import com.zcw.security.rbac.service.ResourceService;
import com.zcw.security.rbac.web.support.SimpleResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName : ResourceController
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:45
*/
@RestController
@RequestMapping("/resource")
public class ResourceController {
@Autowired
private ResourceService resourceService;
/**
* 獲取資源樹
* @param admin
* @return
*/
@GetMapping
public ResourceInfo getTree(@AuthenticationPrincipal Admin admin){
return resourceService.getTree(admin.getId());
}
/**
* 獲取資源信息
* @param id
* @return
*/
@GetMapping("/{id}")
public ResourceInfo getInfo(@PathVariable Long id){
return resourceService.getInfo(id);
}
/**
* 創建資源
* @param info
* @return
*/
@PostMapping
public ResourceInfo create(@RequestBody ResourceInfo info){
if(info.getParentId() == null) {
info.setParentId(0L);
}
return resourceService.create(info);
}
/**
* 修改資源
* @param info
* @return
*/
@PutMapping("/{id}")
public ResourceInfo update(@RequestBody ResourceInfo info){
return resourceService.update(info);
}
/**
* 刪除
* @param id
*/
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){
resourceService.delete(id);
}
/**
* 資源上移
* @param id
*/
@PostMapping("/{id}/up")
public SimpleResponse moveUp(@PathVariable Long id){
return new SimpleResponse(resourceService.move(id, true));
}
/**
* 資源下移
* @param id
*/
@PostMapping("/{id}/down")
public SimpleResponse moveDown(@PathVariable Long id){
return new SimpleResponse(resourceService.move(id, false));
}
}
package com.zcw.security.rbac.web.controller;
import com.zcw.security.rbac.dto.RoleInfo;
import com.zcw.security.rbac.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @ClassName : RoleController
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:47
*/
@RestController
@RequestMapping("/role")
public class RoleController {
@Autowired
private RoleService roleService;
/**
* 創建角色
* @param roleInfo
* @return
*/
@PostMapping
public RoleInfo create(@RequestBody RoleInfo roleInfo) {
return roleService.create(roleInfo);
}
/**
* 修改角色信息
* @param roleInfo
* @return
*/
@PutMapping("/{id}")
public RoleInfo update(@RequestBody RoleInfo roleInfo) {
return roleService.update(roleInfo);
}
/**
* 刪除角色
* @param id
*/
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
roleService.delete(id);
}
/**
* 獲取角色詳情
* @param id
* @return
*/
@GetMapping("/{id}")
public RoleInfo getInfo(@PathVariable Long id) {
return roleService.getInfo(id);
}
/**
* 獲取所有角色
* @param roleInfo
* @param pageable
* @return
*/
@GetMapping
public List<RoleInfo> findAll() {
return roleService.findAll();
}
/**
* 獲取角色的所有資源
* @param id
* @return
*/
@GetMapping("/{id}/resource")
public String[] getRoleResources(@PathVariable Long id){
return roleService.getRoleResources(id);
}
/**
* 創建用戶的資源
* @param id
* @param ids
*/
@PostMapping("/{id}/resource")
public void createRoleResource(@PathVariable Long id, String ids){
roleService.setRoleResources(id, ids);
}
}
package com.zcw.security.rbac.web.support;
/**
* @ClassName : SimpleResponse
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:48
*/
public class SimpleResponse {
public SimpleResponse(Object content){
this.content = content;
}
private Object content;
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
}
package com.zcw.security.rbac;
import com.zcw.security.rbac.domain.Resource;
import org.springframework.stereotype.Repository;
@Repository
public interface ResourceRepository extends ZcwRepository<Resource>{
Resource findByName(String name);
}
package com.zcw.security.rbac;
import com.zcw.security.rbac.domain.RoleAdmin;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleAdminRepository extends ZcwRepository<RoleAdmin> {
}
package com.zcw.security.rbac;
import com.zcw.security.rbac.domain.Role;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleRepository extends ZcwRepository<Role>{
}
package com.zcw.security.rbac;
import com.zcw.security.rbac.domain.RoleResource;
import org.springframework.stereotype.Repository;
/**
* @ClassName : RoleResourceRepository
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-06 19:35
*/
@Repository
public interface RoleResourceRepository extends ZcwRepository<RoleResource> {
}
package com.zcw.security.rbac;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface ZcwRepository<T> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> {
}