SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只贴了部分代码说明,完整项目地址 : https://github.com/EalenXie/shiro-rbac-system

1 . 新建一个项目名为shiro-rbac-system,pom.xml加入基本依赖 :




    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.3.RELEASE
    
    name.ealen
    shiro-rbac-system
    0.0.1
    shiro-rbac-system
    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    
        1.8
        EalenXie
        SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计
    

    
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.apache.shiro
            shiro-spring
            1.3.2
        
        
            com.alibaba
            fastjson
            1.2.31
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

基本的数据对象关系 :

一个用户对应一个或者多个角色。
一个角色对应一个或者多个权限。
一个权限对应能够访问对应的API或url资源。

1 . RBAC基本实体关系,Permission类(权限资源) :


/**
 * Created by EalenXie on 2019/3/25 11:15.
 * 

* 权限许可(Permission) 操作 及其能访问url 权限对应一个url地址 */ @Entity @Table(name = "system_shiro_permission") public class Permission extends BaseEntity { @Column(unique = true) private String name; //权限名 唯一 @Column(unique = true) private String url; //访问地址信息 唯一 private String description; //描述信息 //省略getter/setter }

2 . Role类(用户角色),一个角色拥有一个或者多个权限 :


/**
 * Created by EalenXie on 2019/3/25 11:18.
 * 

* 角色(Role) 角色下面对应多个权限 */ @Entity @Table(name = "system_shiro_role") public class Role extends BaseEntity { @Column(unique = true) private String name; //角色名 唯一 private String description; //描述信息 @ManyToMany(fetch= FetchType.EAGER) private List permissions; //一个用户角色对应多个权限 //省略getter/setter }

3 . User类(用户),一个用户拥有一个或者多个角色 :


/**
 * Created by EalenXie on 2019/3/25 11:01.
 * 

* 用户表(User) 用户下面对应多个角色 */ @Entity @Table(name = "system_shiro_user") public class User extends BaseEntity { @Column(unique = true) private String username;//用户名 唯一 private String password;//用户密码 private String passwordSalt;//用户密码加密盐值 @ManyToMany(fetch = FetchType.EAGER) private List roles;//用户角色 一个用户可能有一个角色,也可能有 多个角色 //省略getter/setter }

2 . 基本配置信息 :

1 . 配置应用的基本信息,application.yml :


server:
  port: 8082
spring:
  application:
    name: shiro-rbac-system
  resources:
    static-locations: classpath:/
  freemarker:
    template-loader-path: classpath:/templates/
    suffix: .html
    content-type: text/html
    charset: UTF-8
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/yourdatabase?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: yourname
    password: yourpass
  jpa:
#    show-sql: true
    hibernate:
      ddl-auto: update
    open-in-view: false # 禁用 OSIV 
  http:
    encoding:
      charset: utf-8
      enabled: true

2 . 配置JPA,基本的数据库访问Dao。


public interface PermissionRepository extends JpaRepository {
    Permission findByName(String name);
}

public interface RoleRepository extends JpaRepository {
    Role findByName(String name);
}


public interface UserRepository extends JpaRepository {
    User findByUsername(String username);
}

3 . 配置shiro,基于角色访问控制权限的核心Realm,UserAuthRealm :


@Component
public class UserAuthRealm extends AuthorizingRealm {

    @Resource
    private UserRepository userRepository;

    /**
     * 权限核心配置 根据数据库中的该用户 角色 和 权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        for (Role role : user.getRoles()) {                                 //获取 角色
            authorizationInfo.addRole(role.getName());                      //添加 角色
            for (Permission permission : role.getPermissions()) {           //获取 权限
                authorizationInfo.addStringPermission(permission.getName());//添加 权限
            }
        }
        return authorizationInfo;
    }

    /**
     * 用户登陆 凭证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userRepository.findByUsername(username);
        if (user == null) return null;
        String credentials = user.getPasswordSalt() + user.getUsername() + user.getPasswordSalt();//自定义加盐 salt + username + salt
        return new SimpleAuthenticationInfo(
                user, //用户名
                user.getPassword(), //密码
                ByteSource.Util.bytes(credentials), //加密
                getName()  //realm name
        );
    }

    /**
     * 设置 realm的 HashedCredentialsMatcher
     */
    @PostConstruct
    public void setHashedCredentialsMatcher() {
        this.setCredentialsMatcher(hashedCredentialsMatcher());
    }

    /**
     * 凭证匹配 : 指定 加密算法,散列次数
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }
}

4 . shiro核心配置,包含基本过滤器策略,注解支持等。


/**
 * Created by EalenXie on 2019/3/25 15:12.
 */
@Configuration
public class ShiroConfig {

    @Resource
    private PermissionRepository permissionRepository;

    @Resource
    private UserAuthRealm userAuthRealm;

    /**
     * 配置 资源访问策略 . web应用程序 shiro核心过滤器配置
     */
    @Bean
    public ShiroFilterFactoryBean factoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl("/login");//登录页
        factoryBean.setSuccessUrl("/index");//首页
        factoryBean.setUnauthorizedUrl("/unauthorized");//未授权界面;
        factoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap()); //配置 拦截过滤器链
        return factoryBean;
    }

    /**
     * 配置 SecurityManager,可配置一个或多个realm
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userAuthRealm);
//        securityManager.setRealm(xxxxRealm);
        return securityManager;
    }

    /**
     * 开启shiro 注解支持. 使以下注解能够生效 :
     * 需要认证 {@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}
     * 需要用户 {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}
     * 需要访客 {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}
     * 需要角色 {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}
     * 需要权限 {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 配置 拦截过滤器链.  map的键 : 资源地址 ;  map的值 : 所有默认Shiro过滤器实例名
     * 默认Shiro过滤器实例 参考 : {@link org.apache.shiro.web.filter.mgt.DefaultFilter}
     */
    private Map setFilterChainDefinitionMap() {
        Map filterMap = new LinkedHashMap<>();
        //注册 数据库中所有的权限 及其对应url
        List allPermission = permissionRepository.findAll();//数据库中查询所有权限
        for (Permission p : allPermission) {
            filterMap.put(p.getUrl(), "perms[" + p.getName() + "]");    //拦截器中注册所有的权限
        }
        filterMap.put("/static/**", "anon");    //公开访问的资源
        filterMap.put("/open/api/**", "anon");  //公开接口地址
        filterMap.put("/logout", "logout");     //配置登出页,shiro已经帮我们实现了跳转
        filterMap.put("/**", "authc");          //所有资源都需要经过验证
        return filterMap;
    }
}

5 . 用于授权相关注解进行测试的API RestController。AuthorizationApiFacade :


/**
 * Created by EalenXie on 2019/3/26 16:46.
 * 授权相关API 测试
 */
@RestController
public class AuthorizationApiFacade {


    /**
     * 需要 验证 才能访问的api
     */
    @RequestMapping("/requiresAuthentication")
    @RequiresAuthentication
    public Map requiresAuthentication() {
        Map result = new HashMap<>();
        result.put("msg", "Require Authentication : 需要认证 测试, 能够访问此接口");
        return result;
    }

    /**
     * 需要 用户 身份才能访问的api
     */
    @RequiresUser
    @RequestMapping("/requiresUser")
    public Map requiresUser() {
        Map result = new HashMap<>();
        result.put("msg", "Require User : 需要用户 测试, 能够访问此接口");
        return result;
    }

    /**
     * 需要 Guest 身份才能访问的api
     */
    @RequiresGuest
    @RequestMapping("/requiresGuest")
    public Map requiresGuest() {
        Map result = new HashMap<>();
        result.put("msg", "Require Guest : 需要认证 测试, 能够访问此接口");
        return result;
    }

    /**
     * 需要 administrator 角色才能访问的api
     */
    @RequiresRoles("administrator")
    @RequestMapping("/requiresRoles")
    public Map requiresRoles() {
        Map result = new HashMap<>();
        result.put("msg", "require Roles : 该用户具有 administrator 角色, 能够访问此接口");
        return result;
    }

    /**
     * 需要 add 权限才能访问的api
     */
    @RequiresPermissions("add")
    @RequestMapping("/requiresPermissionsAdd")
    public Map requiresPermissionsAdd() {
        Map result = new HashMap<>();
        result.put("msg", "require Permissions : 该用户具有 add 权限 , 能够访问此接口");
        return result;
    }

    /**
     * 需要 delete 权限才能访问的api
     */
    @RequiresPermissions("delete")
    @RequestMapping("/requiresPermissionsDelete")
    public Map requiresPermissionsDelete() {
        Map result = new HashMap<>();
        result.put("msg", "require Permissions : 该用户具有 delete 权限 , 能够访问此接口");
        return result;
    }


    /**
     * 公开接口
     */
    @RequestMapping(value = "/open/api/sayHello", method = RequestMethod.POST)
    public Map sayHello() {
        Map result = new HashMap<>();
        result.put("msg", "这个是公开接口,谁都可以访问");
        return result;
    }
}

进行测试,用户登陆(zhangsan)测试,zhangsan只具有user角色,只有部分权限。

image

登陆成功后,进入到首页。

image

访问,/add 则跳转到 新增页面

image

访问/delete,因为没有权限会跳转到未授权页面。

image

zhangsan只能调用自己拥有角色和权限的api :

image
image

没有相关角色和权限的api不能调用 :

image

你可能感兴趣的:(SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建)