【Spring Security技術棧開發企業級認證與授權】-----------使用Spring Security控制授權

前言

本篇博客主要分享: 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> {

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章