shiro springboot 授权

2020-08-25  本文已影响0人  杨健kimyeung

一、概要

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

二、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
上一篇 下一篇

猜你喜欢

热点阅读