Shiro 几种Realm的使用,认证、授权

目录

常用的Realm有3种

  • IniRealm:从ini文件加载安全数据
  • JdbcRealm:从数据库加载安全数据
  • 自定义Realm:开发中最常用

principal : 主体(Subject)的唯一标识,可以有多个principal,常见的比如用户名,手机号,邮箱等
credential:凭证, 一般是密码

如果有多个权限实体,权限可以写成 实体:权限 的形式,eg. video:watch,video:download,user:find,user:update

Shiro的依赖

shiro的依赖有很多,最常用的是 shiro-spring 。

IniRealm

1、resources下新建shiro.ini

文件名任意,因为后续要在代码中指定ini文件名

#用户信息
[users]
#用户名=密码,角色  一个用户可以对应多个角色,eg.同时是黄钻、绿钻,对应多个角色时,角色逗号分隔
chy1 = abcd, common
chy2 = abcd, vip
chy3 = abcd, svip

#角色信息
[roles]
#角色=权限   一个角色可以有多个权限,有多个权限时,权限逗号分隔。*表示具有全部权限
common = watch
vip = watch,download
svip = *

2、shiro工具类

/**
 * Shiro工具类
 */
public class ShiroUtil {

    //初始化
    static {
        //初始化IniRealm,指定shiro的ini配置文件
        IniRealm iniRealm = new IniRealm("classpath:shiro.ini");

        //配置SecurityManager
        DefaultSecurityManager securityManager = new DefaultSecurityManager(iniRealm);

        //设置SecurityUtils要使用的SecurityManager
        SecurityUtils.setSecurityManager(securityManager);
    }


   //shiro的认证、授权操作都需要先将User包装为Subject,通过Subject来操作


    /**
     * 登录校验
     * @return boolean 账户、密码是否匹配
     */
    public static boolean login(String username,String password) {
        Subject subject = SecurityUtils.getSubject();
        //用token封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);  //将token与Realm中的安全数据对比。返回值是void,如果找不到匹配,直接抛出异常
        } catch (AuthenticationException e) {  //Realm中没有匹配的用户
            return false;
        }

        return true;
    }


    /**
     * 登出
     */
    public static void logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
    }


    /**
     * 检查用户是否拥有指定的角色
     * @param role
     * @return boolean 该用户是否具有指定的角色
     */
    public static boolean hasRole(String role) {
        Subject subject = SecurityUtils.getSubject();
        return subject.hasRole(role);
    }


    /**
     * 获取用户对应的角色
     * @return String 用户对应的角色
     */
    public static String getRole(){
        Subject subject = SecurityUtils.getSubject();
        String[] allRole = {"common", "vip", "svip"};

        //如果用户同时具有多个角色,比如同时是绿钻、黄钻,可以放在数组、集合中返回
        for (String role:allRole) {
            if (subject.hasRole(role)) {
                return role;
            }
        }

        return null;
    }


    /**
     * 检查用户是否拥有指定的权限
     * @param permit 要检查的权限
     * @return boolean 该用户是否具有指定的权限
     */
    public static boolean isPermitted(String permit) {
        Subject subject = SecurityUtils.getSubject();
        return subject.isPermitted(permit);
    }

}

导入类时,如果有同名的类,是导入shiro中的

JdbcRealm

安全数据一般存储在数据库中,一般都是从数据库中加载安全数据。

JdbcRealm中的部分源码

protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
protected String authenticationQuery = "select password from users where username = ?";
protected String userRolesQuery = "select role_name from user_roles where username = ?";
protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";

表名、字段名都是固定的

扫描二维码关注公众号,回复: 14268872 查看本文章

1、数据表

  • 用户表users:需包含username、password、password_salt字段,用于指定用户名、密码
  • 角色表user_roles:需包含username、role_name字段,用于指定用户对应的角色
  • 权限表roles_permissions:需包含role_name、permission字段,用于指定角色对应的权限

以上字段均为varchar类型,其中password_salt字段的值可为null。3张表都可以新建id列作为主键,也可以直接使用现有的列作为主键(视业务而定)。

user_roles表通过username与users表关联,roles_permissions表通过role_name与user_roles表关联。

username唯一标识用户,不可重复。如果一个用户具有多种角色,写成多条记录的形式。如果一个角色具有多种权限,都写在permission字段中,英文逗号隔开,*表示具有全部权限。

2、shiro工具类

/**
 * Shiro工具类
 */
public class ShiroUtil {

    //初始化
    static {
        //初始化数据源
        HikariDataSource dataSource=new HikariDataSource();
        {
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("abcd");
        }

        //初始化JdbcRealm
        JdbcRealm jdbcRealm=new JdbcRealm();
        jdbcRealm.setDataSource(dataSource);
        jdbcRealm.setPermissionsLookupEnabled(true); //默认为false,需设置为true才能进行授权

        //初始化SecurityManager
        DefaultSecurityManager securityManager = new DefaultSecurityManager(jdbcRealm);

        //设置SecurityUtils要使用的SecurityManager
        SecurityUtils.setSecurityManager(securityManager);
    }


    //shiro的认证、授权操作都需要先将User包装为Subject,通过Subject来操作


    /**
     * 登录检查
     * @return boolean 账号、密码是否匹配
     */
    public static boolean login(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        //使用token封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            //将token与Realm中的信息进行对比。返回值是void,如果在Realm中找不到匹配,直接抛出异常
            subject.login(token);
        } catch (AuthenticationException e1) {  //Realm中没有匹配的用户
            return false;
        }

        return true;
    }


    /**
     * 登出
     */
    public static void logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
    }


    /**
     * 检查用户是否拥有指定的角色
     * @param role 角色
     * @return boolean 该用户是否具有指定的角色
     */
    public static boolean hasRole(String role) {
        Subject subject = SecurityUtils.getSubject();
        return subject.hasRole(role);
    }


    /**
     * 获取用户对应的角色
     * @return String 用户对应的角色
     */
    public static String getRole(){
        Subject subject = SecurityUtils.getSubject();
        String[] allRole = {"common", "vip", "svip"};

        //如果用户同时具有多个角色,比如同时是绿钻、黄钻,可以放在数组、集合中返回
        for (String role:allRole){
            if (subject.hasRole(role)){
                return role;
            }
        }

        return null;
    }


    /**
     * 检查用户是否拥有指定的权限
     * @param permit 要检查的权限
     * @return boolean 该用户是否具有指定的权限
     */
    public static boolean isPermitted(String permit) {
        Subject subject = SecurityUtils.getSubject();
        return subject.isPermitted(permit);
    }

}

自定义Realm

IniRealm、JdbcRealm写死了校验规则,只能使用特定的表名,往往不符合需求,开发中一般使用自定义的Realm。

1、数据表设计

  • tb_user:可以添加salt字段
  • tb_role
  • tb_permission:有多个需要验证权限的实体时,写成 实体:权限 或 实体_权限的形式,eg. video:watch 或 video_watch
  • tb_user_role、tb_role_permission:此处是多对多,建立中间表;也可能是一对多,引入外键关联即可,具体视业务需求而定

2、新建类shiro.CustomRealm,继承AuthorizingRealm

/**
 * 自定义的Realm
 */
public class CustomRealm extends AuthorizingRealm {

    /**
     * 授权方法,权限校验时自动调用
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取主体标识
        String username = (String) principalCollection.getPrimaryPrincipal();

        //使用dao层,根据主体标识查询用户对应的角色、权限,此处略过
        Set<String> permissions = null;
        Set<String> roles = null;

        //设置并返回主体的授权信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }


    /**
     * 认证方法,登录时自动调用
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取主体标识
        String username = (String) token.getPrincipal();

        //使用dao层,根据主体标识查询用户正确的密码,此处略过
        String pwd = null;

        //如果账号、密码不匹配,返回null
        if (pwd==null || pwd.equals("")){
            return null;
        }

        //设置并返回主体的认证信息,主体标识、凭证、realm的名称
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, pwd, this.getName());

        return simpleAuthenticationInfo;
    }

}

导入的类有多个候选项时,导入shiro中的

3、shiro工具类

/**
 * Shiro工具类
 */
public class ShiroUtil {

    //初始化
    static {
        //初始化Realm
        CustomRealm customRealm = new CustomRealm();

        //初始化SecurityManager
        DefaultSecurityManager securityManager = new DefaultSecurityManager(customRealm);

        //设置SecurityUtils要使用的SecurityManager
        SecurityUtils.setSecurityManager(securityManager);
    }


    // shiro的认证、授权操作都需要先将User包装为Subject,通过Subject来操作


    /**
     * 登录校验
     * @return boolean 账号、密码是否匹配
     */
    public static boolean login(String username,String password) {
        Subject subject = SecurityUtils.getSubject();
        //使用token封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        
        try {
            //将token与Realm中的安全数据对比。返回值是void,如果在Realm中找不到匹配,直接抛出异常
            subject.login(token);
        } catch (AuthenticationException e1) {  //Realm中没有匹配的用户
            return false;
        }
        
        return true;
    }


    /**
     * 登出
     */
    public static void logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
    }


    /**
     * 检查用户是否拥有指定的角色
     * @param role
     * @return boolean 该用户是否拥有指定的角色
     */
    public static boolean hasRole(String role) {
        Subject subject = SecurityUtils.getSubject();
        return subject.hasRole(role);
    }


     /**
     * 获取用户对应的所有角色
     * @return Set<String> 用户对应的所有角色
     */
    public static Set<String> getRoles(){
        Subject subject = SecurityUtils.getSubject();
        Set<String> allRole = null;  //系统所有的角色,此处略过
        Set<String> roles = new HashSet<>(allRole.size());  //subject具有的所有角色

        for (String role:allRole){
            if (subject.hasRole(role)){
                roles.add(role);
            }
        }

        return roles;
    }


    /**
     * 检查用户是否具有指定权限
     * @param permit 要检查的权限
     * @return boolean 该用户是否具有指定的权限
     */
    public static boolean isPermitted(String permit) {
        Subject subject = SecurityUtils.getSubject();
        return subject.isPermitted(permit);
    }

}

使用示例

if ( ShiroUtil.login("chy","abcd") ){
    System.out.println("登录成功");
    System.out.println("您的身份是:" + ShiroUtil.getRole());
    System.out.println("是否具有download权限:" + ShiroUtil.isPermitted("download"));
}
else {
    System.out.println("用户名或密码错误");
}

猜你喜欢

转载自blog.csdn.net/m0_67393157/article/details/124343029
今日推荐