美文网首页
shiro springboot 授权

shiro springboot 授权

作者: 杨健kimyeung | 来源:发表于2020-08-25 13:19 被阅读0次

    一、概要

    授权也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

    • 主体:即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。
    • 资源:在应用中用户可以访问的任何东西,比如访问界面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
    • 权限:安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:
      访问用户列表页面
      查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)。打印文档等等
      如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro不会去做这件事情,而是由实现人员提供。
      Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)
    • 角色:角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
    • 隐式角色:即直接通过角色来验证用户有没有操作权限,如在应用中CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。
    • 显示角色:在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

    二、Shiro 的授权方式

    Shiro支持三种方式的授权:编程式、注解式、JSP/GSP标签

    注解方式 (常用)

    通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常;

    @RequiresRoles("admin")
    public void index() {
        //有权限
    }
    

    编程方式(了解)

    通过hasRole写 if/else 授权代码块完成

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
        //有权限
    } else {
        //无权限
    }
    // 或者
    if (subject.isPermitted("user:create")) {
      log.info(subject.getPrincipal()+ ":" + "可以创建用户");
    }
    

    JSP/GSP 标签(了解)

    在 JSP/GSP页面中使用相应的标签

    <shiro:hasRole name="admin">
    <!— 有权限 —>
    </shiro:hasRole>
    

    三、注解控制鉴权授权

    注解 功能
    @RequiresGuest 只有游客可以访问
    @RequiresAuthentication 需要登录才能访问
    @RequiresUser 已登录的用户或“记住我”的用户能访问
    @RequiresRoles 已登录的用户需具有指定的角色才能访问
    @RequiresPermissions 已登录的用户需具有指定的权限才能访问

    四、步骤

    1、配置shiro注解

    需要注意的时候如果使用RequiresPermissions等注解 访问的时候出现404

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
      DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
      /**
        * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
        * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,导致返回404。
        */
      defaultAdvisorAutoProxyCreator.setUsePrefix(true);
      return defaultAdvisorAutoProxyCreator;
    }
    
    /**
     * Shiro生命周期处理器
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
      return new LifecycleBeanPostProcessor();
    }
    

    过滤器

    @Bean
    public ShiroFilterChainDefinition filterChainDefinition() {
        //登录接口不需要认证就能访问
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        // 表示不需要授权  (anon 表示访问登录不需要认证)
        definition.addPathDefinition("/login", "anon");
        definition.addPathDefinition("/register", "anon");
        definition.addPathDefinition("/druid/**", "anon");
        definition.addPathDefinition("/index", "anon");
        // 用户登出的操作
        definition.addPathDefinition("/logout", "logout");
        // anonc 表示所有的api开头的url地址必须登录 403
        definition.addPathDefinition("/api/**", "anonc");
        definition.addPathDefinition("/**", "anonc");
        definition.addPathDefinition("/admin/**", "anonc");
        return definition;
    }
    

    2、自定义授权

    public class UserRealm extends AuthorizingRealm {
        @Resource
        RolePermissionMapper rolePermissionMapper;
    
        /**
         * 授权
         * 权限表的设计
         * 使用权限
         * 注解
         *
         *
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //获取的是通过认证 simpleAuthenticationInfo对象的第一个参数  如果第一个参数是对象 则可以墙砖成对象,如果是String类型
            User user = (User) principalCollection.getPrimaryPrincipal();
    //      通过用户信息查询角色表 权限表
            Integer uid = user.getUid();
            List<RoleDto> roleList = rolePermissionMapper.selectRolesByUserId(uid);
            // 角色集合
            Set<String> roles = new HashSet<>();
            // 权限集合
            List<String> permissions = new ArrayList<>();
            for (RoleDto role : roleList) {
                roles.add(role.getRoleName());
                for (PermissionDto permission : role.getPermissions()) {
                    permissions.add(permission.getPerName());
                }
            }
            
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.setRoles(roles);
            info.addStringPermissions(permissions);
            return info;
        }
    }
    

    在控制层中

    @RestController
    public class TestRoleController {
        @GetMapping("/")
        public String index() {
            return "不需要热河权限";
        }
    
        // 只有admin角色才能访问的该接口
        @GetMapping("/role/admin")
        @RequiresRoles(value = {"admin", "user"}, logical = Logical.OR)
        public String testAdmin() {
            return "只有admin角色才能访问";
        }
    
        @GetMapping("/per/create")
        @RequiresPermissions("user:create")
        public String testPermission() {
            return "创建用户信息的权限";
        }
    }
    

    数据库表

    create table sys_permission
    (
        per_id      int auto_increment comment '主键'
            primary key,
        per_name    varchar(128)  not null comment '权限名称',
        description varchar(255)  null comment '权限说明',
        is_del      int default 0 null comment '是否启用',
        constraint per_name
            unique (per_name)
    )
        comment '权限表';
    
    create table sys_role
    (
        role_id     int auto_increment comment '主键'
            primary key,
        role_name   varchar(128)  not null comment '角色的名称',
        description varchar(255)  null comment '角色说明',
        is_del      int default 0 null comment '是否启用',
        constraint role_name
            unique (role_name)
    )
        comment '权限表';
    
    create table sys_role_permission
    (
        rpid    int auto_increment
            primary key,
        per_id  int not null comment '权限ID',
        role_id int not null comment '角色ID'
    )
        comment '角色权限表';
    
    create table sys_user
    (
        uid      int auto_increment
            primary key,
        username varchar(20)   not null,
        password varchar(128)  not null,
        status   int default 0 null,
        constraint username
            unique (username)
    )
        comment '用户表';
    
    create table sys_user_role
    (
        id      int auto_increment
            primary key,
        uid     int not null comment '用户ID',
        role_id int not null comment '角色ID'
    )
        comment '用户角色表';
    
    
    

    工具类

    public class ShiroUtils {
    
        /**
         * 循环次数
         */
        public final static int HASH_ITERATIONS = 1024;
    
        public static String sha256(String password, String salt) {
            return new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, salt, HASH_ITERATIONS).toString();
        }
    
        // 获取一个测试账号 admin
        public static void main(String[] args) {
            // 3743a4c09a17e6f2829febd09ca54e627810001cf255ddcae9dabd288a949c4a
            System.out.println(sha256("admin", "123"));
        }
    
        /**
         * 获取会话
         */
        public static Session getSession() {
            return SecurityUtils.getSubject().getSession();
        }
    
        /**
         * Subject:主体,代表了当前“用户”
         */
        public static Subject getSubject() {
            return SecurityUtils.getSubject();
        }
    
        public static User getUser() {
            return (User) SecurityUtils.getSubject().getPrincipal();
        }
    
        public static Integer getUserId() {
            return getUser().getUid();
        }
    
        public static void setSessionAttribute(Object key, Object value) {
            getSession().setAttribute(key, value);
        }
    
        public static Object getSessionAttribute(Object key) {
            return getSession().getAttribute(key);
        }
    
        public static boolean isLogin() {
            return SecurityUtils.getSubject().getPrincipal() != null;
        }
        
        public static void logout() {
            SecurityUtils.getSubject().logout();
        }
    }
    

    3、自定义权限异常提示

    @RestControllerAdvice
    public class ShiroException {
      @ExceptionHandler(AuthorizationException.class)
      public String authorizationException (){
        return "抱歉您没有权限访问该内容!";
      }
      @ExceptionHandler(Exception.class)
      public String handleException(Exception e){
        return "系统异常!";
      }
    }
    

    附、常见的API

    1、判断当前用户是否拥有角色

    1. 常用方法

      Subject方法 描述
      hasRole(String roleName) 当用户拥有指定角色时,返回true
      hasRoles(List<String> roleNames) 按照列表顺序返回相应的一个boolean值数组
      hasAllRoles(Collection<String> roleNames) 如果用户拥有所有指定角色时,返回true
    2. 示例代码

      if (currentUser.hasRole("admin")) {
        log.info("角色:" + "admin");
      }
      

    2、判断当前用户是否拥有权限

    1. 常用API

      Subject方法 描述
      checkRole(String roleName) 断言用户是否拥有指定角色
      checkRoles(Collection<String> roleNames) 断言用户是否拥有所有指定角色
      checkRoles(String... roleNames) 对上一方法的方法重载
    2. 示例代码

      //判断用户是否有权限
      if (currentUser.isPermitted("user:create")) {
        log.info(currentUser.getPrincipal()+ ":" + "可以创建用户");
      }
      

    3、过滤器

    Filter 说明 对应的烂机器
    anon(常用) 无参,开放权限,可以理解为匿名用户或游客 org.apache.shiro.web.filter.authc.AnonymousFilter
    authc(常用) 无参,需要认证 org.apache.shiro.web.filter.authc.FormAuthenticationFilter
    logout(固定) 无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url org.apache.shiro.web.filter.authz.PortFilter
    authcBasic 无参,表示 httpBasic 认证 org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
    user 无参,表示必须存在用户,当登入操作时不做检查 org.apache.shiro.web.filter.authc.UserFilter
    ssl 无参,表示安全的URL请求,协议为 https org.apache.shiro.web.filter.authz.SslFilter
    perms[user] 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
    roles[admin] 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过 org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
    rest[user] 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
    port[8081] 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数 org.apache.shiro.web.filter.authz.PortFilter

    相关文章

      网友评论

          本文标题:shiro springboot 授权

          本文链接:https://www.haomeiwen.com/subject/jszajktx.html