Shiro基础教程

        Apache Shiro是一个Java安全框架,执行身份验证、授权、密码和会话管理。

三个核心组件:Subject, SecurityManager 和 Realms.

  1. Subject:即“当前操作用户”。主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
  2. SecurityManager:安全管理器;它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  3. Realm域, Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。

(1)使用用户的登录信息创建令牌

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

token可以理解为用户令牌,登录的过程被抽象为Shiro验证令牌是否具有合法身份以及相关权限。

(2)执行登陆动作

SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManager
Subject subject = SecurityUtils.getSubject(); // 获取Subject单例对象
subject.login(token); // 登陆

Shiro的核心部分是SecurityManager,它负责安全认证与授权。Shiro本身已经实现了所有的细节,用户可以完全把它当做一个黑盒来使用。SecurityUtils对象,本质上就是一个工厂类似Spring中的ApplicationContext。Subject是初学者比较难于理解的对象,很多人以为它可以等同于User,其实不然。Subject中文翻译:项目,而正确的理解也恰恰如此。它是你目前所设计的需要通过Shiro保护的项目的一个抽象概念。通过令牌(token)与项目(subject)的登陆(login)关系,Shiro保证了项目整体的安全。

(3)判断用户

Shiro本身无法知道所持有令牌的用户是否合法,因为除了项目的设计人员恐怕谁都无法得知。因此Realm是整个框架中为数不多的必须由设计者自行实现的模块,当然Shiro提供了多种实现的途径,本文只介绍最常见也最重要的一种实现方式——数据库查询。AuthenticationInfo代表了用户的角色信息集合,AuthorizationInfo代表了角色的权限信息集合。

          Shiro通过一系列filter来控制访问权限,内部为我们预先定义了多个过滤器,我们可以直接通过字符串配置这些过滤器。

authc:所有已登陆用户可访问

roles:有指定角色的用户可访问,通过[ ]指定具体角色,这里的角色名称与数据库中配置一致

perms:有指定权限的用户可访问,通过[ ]指定具体权限,这里的权限名称与数据库中配置一致

anon:所有用户可访问,通常作为指定页面的静态资源时使用、

user:如果使用RememberMe的功能可以直接访问

ShiroConfig:

@Configuration
public class ShiroConfig {
    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        //添加Shiro过滤器
        Map<String,String> map = new LinkedHashMap<>();
        //添加授权信息,为每个请求地址设置访问权限
        //perms后括号内的参数设置:为该请求设置一个名称,后期将权限名称保存在数据库中,通过查询用户是否有这些名称来判断用户是否拥有此类权限
        map.put("/","anon");
        map.put("/index","authc");
        map.put("/delDepart","perms[depart:del]");
        map.put("/editDepart","perms[depart:edit]");
        map.put("/addDepart","perms[depart:add]");
        map.put("/delUser","perms[user:del]");
        map.put("/editUser","perms[user:edit]");

        //设置未授权跳转页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noPerm");
        //设置未登录跳转页面
        shiroFilterFactoryBean.setLoginUrl("/");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    /**
     * 创建DefaultWebSecurityManager安全管理器
     */
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //关联Reaml
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    /**
     * 创建Realm
     */
    @Bean(name = "userRealm")
    public UserRealm getReaml(){
        return new UserRealm();
    }
}

Realm实现:

//Realm:用于连接数据
public class UserRealm extends AuthorizingRealm {
    //用户service
    @Autowired
    UserService userService;
    //角色service
    @Autowired
    RoleService roleService;
    //用户权限service
    @Autowired
    RolePermService rolePermService;
    /**
     * 重写执行授权逻辑的方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑!");
        //给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //创建一个用户主体,来获取登录的用户信息
        Subject subject = SecurityUtils.getSubject();
        //user对象是通过楼下认证逻辑方法中SimpleAuthenticationInfo传递的
        Users user1 = (Users) subject.getPrincipal();
        //通过获取到的用户对象,使用用户对象中的角色id去用户权限表(rolePerm)中或获取该角色的权限
        List<String> list = new ArrayList<>();
        list = rolePermService.findAllPermCode(user1.getRole_id());
        //添加资源的授权字段,此时该用户以及完成被授权的所有操作
        info.addStringPermissions(list);
        return info;
    }

    /**
     * 重写执行认证逻辑的方法:主要用来做用户登录认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑!");

        //编写shiro判断逻辑,验证用户名和密码
        //判断用户名(通过controller层发送过来的用户token中封装的用户的账号密码)
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        Users user = new Users();
        user = userService.findUserByName(token.getUsername());
        if (user==null){
            return null;//shiro底层会抛出UnknownAccountException
        }
        //判断密码,第一个参数:传一个user对象给楼上授权逻辑的subject.getPrincipal();第二个参数:获取数据库中的密码,与传来的token中的密码对比;第三个参数:getName() 当前的realm名,可不写
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

shiro登录(controller):

 @RequestMapping("/doLogin")
    public String doLogin(String username, String password, HttpServletRequest request){
        /**
         * Shrio认证
         */
        //1、获取Subject
        Subject subject = SecurityUtils.getSubject();
        //2、封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        //3、执行登录方法
        try{
            //登录验证,会去找Reaml的doGetAuthenticationInfo进行逻辑认证
            subject.login(token);
            //验证成功跳转到index
            return "redirect:/index";

        }catch (UnknownAccountException e){
        //UnknownAccountException:用户名找不到异常,return到登录页面
            return "redirect:/";
        }catch (IncorrectCredentialsException e){
        //IncorrectCredentialsException:密码错误异常,return到登录页面
            return "redirect:/";
        }
     }

ps:

  1. Authenticator:认证器,负责Subject的认证操作,认证过程就是根据Subject提供的信息通过Realm查询到相关信息,然后做对比,支持扩展

  2. Authorizer:授权器,控制着Subject对服务资源的访问权限

  3. SessionManager:用于管理Session,这个Session可以是web的也可以不是web的。

  4. SessionDao:把Session的 CRUD和存储介质联系起来的工具,存储介质可以是数据库,也可以是缓存,比如把session放到redis里面

  5. CacheManager:缓存控制器,Realm管理的数据(用户、角色、权限)可以放到缓存里由CacheManager管理,提高认证授权等的速度

  6. Cryptography:加密组件,Shiro提供了很多加解密算法的组件

Tips:

      • [urls] 部分的配置,其格式是: “url=拦截器[参数],拦截器[参数]”;
      • 如果当前请求的 url 匹配 [urls] 部分的某个 url 模式,将会执行其配置的拦截器。
      • anon(anonymous) 拦截器表示匿名访问(即不需要登录即可访问)
      • authc (authentication)拦截器表示需要身份认证通过后才能访问

2)shiro中的默认过滤器

3)URL 匹配模式

     • url 模式使用 Ant 风格模式
     • Ant 路径通配符支持 ?、*、**,注意通配符匹配不包括目录分隔符“/”:

     ?   :匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/;
      *   :匹配零个或多个字符串,如 /admin 将匹配 /admin、/admin123,但不匹配 /admin/1;
      **  :匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b

4)URL 匹配顺序

      •  URL  权限采取第一次匹配优先的 方式,即从头开始使用第一个匹配的 url 模式对应的拦截器链。
      • 如:
             – /bb/**=filter1
             – /bb/aa=filter2
             – /**=filter3
             – 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹配,那么将使用 filter1 进行拦截。
 

发布了62 篇原创文章 · 获赞 94 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37676429/article/details/102680608
今日推荐