• Shiro


     Realm相当于数据源,SecurityManager从Reaml获取相应的用户进行比较

    Shiro的架构

    • Subject:任何可以与用户交互的用户
    • SecurityManager:所有具体的交互都由SecurityManager控制,它管理所有的Subject,切负责进行认证,授权,会话即缓存管理
    • Authenticator:认证器,负责Subject的认证,可以自定义实现。可以使用认证策略来定义什么情况下算用户认证通过
    • Authorizer: 授权器,即访问控制器,确定主体是否有权限进行相应的操作。
    • Realm:用于获取安全实体,可以有一个或多个。一般在用于中都需要自己实现Realm
    • SessionManager:管理Session生命周期的组件。
    • SessionDao:代表SessionManager执行Session持久化操作
    • CacheManager:缓存控制器,用来管理用户,角色,权限等缓存
    • Cryptography:密码模块,加密解密。

    初步使用

    导入依赖

    <dependencies>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.4.1</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
    </dependencies>

    编写Shiro配置

    shiro.ini

    [users]
    root = secret, admin
    guest = guest, guest
    presidentskroob = 12345, president
    darkhelmet = ludicrousspeed, darklord, schwartz
    lonestarr = vespa, goodguy, schwartz
    [roles]
    admin = *
    schwartz = lightsaber:*
    goodguy = winnebago:drive:eagle5

    log4j.properties

    log4j.rootLogger=INFO, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    # General Apache libraries
    log4j.logger.org.apache=WARN
    # Spring
    log4j.logger.org.springframework=WARN
    # Default Shiro logging
    log4j.logger.org.apache.shiro=INFO
    # Disable verbose logging
    log4j.logger.org.apache.shiro.util.ThreadContext=WARN
    log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

    使用shiro API

    package com.deng;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class Quickstart {
        private static final transient Logger log =
                LoggerFactory.getLogger(Quickstart.class);
    
        public static void main(String[] args) {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            Subject currentUser = SecurityUtils.getSubject();
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("Retrieved the correct value! [" + value + "]");
            }
            if (!currentUser.isAuthenticated()) {
                UsernamePasswordToken token = new
                        UsernamePasswordToken("lonestarr", "vespa");
                token.setRememberMe(true);
                try {
                    currentUser.login(token);
                } catch (UnknownAccountException uae) {
                    log.info("There is no user with username of " +
                            token.getPrincipal());
                } catch (IncorrectCredentialsException ice) {
                    log.info("Password for account " +
                            token.getPrincipal() + " was incorrect!");
                } catch (LockedAccountException lae) {
                    log.info("The account for username " +
                            token.getPrincipal() + " is locked. " +
                            "Please contact your administrator to unlock it.");
                } catch (AuthenticationException ae) {
                }
            }
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring. Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }
            currentUser.logout();
            System.exit(0);
        }
    }

    解析上面的代码:

    1.通过工厂模式创建SecurityManager实例对象

    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);

    2.获取当前的subject

    Subject currentUser = SecurityUtils.getSubject();

    3.session操作

    Session session = currentUser.getSession();//获取session
    session.setAttribute("someKey", "aValue");//设置session的值
    String value = (String) session.getAttribute("someKey");//从session中获取值
    if (value.equals("aValue")) {//判断session中是否存在值
           log.info("Retrieved the correct value! [" + value + "]");
    }

    4.用户认证功能

     if (!currentUser.isAuthenticated()) {//当前用户是否已经认证
                //将用户名和密码封装为UsernamePasswordToken
                UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                //开启记住我功能
                token.setRememberMe(true);
                try {
                    currentUser.login(token);//执行登录
                } catch (UnknownAccountException uae) {//如果没有指定用户,抛出该异常
                    log.info("There is no user with username of " +
                            token.getPrincipal());
                } catch (IncorrectCredentialsException ice) {//如果密码不对,抛出该异常
                    log.info("Password for account " +
                            token.getPrincipal() + " was incorrect!");
                } catch (LockedAccountException lae) {//如果用户被锁定抛出该异常
                    log.info("The account for username " +
                            token.getPrincipal() + " is locked. " +
                            "Please contact your administrator to unlock it.");
                } catch (AuthenticationException ae) {//认证异常,上面的异常都是其子类
    
                }
            }
            //getPrincipal()获取标识主体
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

    5.角色检查

           //检测用户是否拥有某一角色
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }

    6.权限检查

           //测试用户是否具有某一权限,行为
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring. Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
            //测试用户是否具有某一权限,行为,比上面更加具体
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }

    7.注销

    currentUser.logout();

    SpringBoot集成

    导入Shiro和Spring整合的依赖

    <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
    </dependency>

    Realm是Shiro的连接数据,创建Realm类继承AuthorizingRealm类,重写认证和授权逻辑

    public class UserRealm extends AuthorizingRealm {
        //执行授权逻辑
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了授权逻辑");
            return null;
        }
        //执行认证逻辑
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了认证逻辑");
            return null;
        }
    }

    编写ShiroConfig配置类,对请求进行过滤

    @Configuration
    public class ShiroConfig {
        //创建ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager")DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            /*添加内置过滤器,常用过滤器如下:
               anon:无需认证就可以访问
               authc:必须认证才可以访问
               user:如果使用了记住我功能就可以直接访问
               perms:拥有某个资源权限才可以访问
               role:拥有某个角色才可以访问
            */
            Map<String,String> filterMap=new LinkedHashMap<>();
            filterMap.put("/user/add","authc");
            filterMap.put("/user/update","authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
            //配置跳转页面
            shiroFilterFactoryBean.setLoginUrl("/toLogin");
            return shiroFilterFactoryBean;
        }
        //创建DefaultWebSecurityManager
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
            DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
            //关联Realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
        //创建realm
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    
    }

    登录认证操作

    Controller

    @Controller
    public class LoginController {
        //登录认证
        @RequestMapping("/login")
        public String login(String username, String password, Model model){
            //获取Subject
            Subject subject=SecurityUtils.getSubject();
            //封装用户数据
            UsernamePasswordToken token=new UsernamePasswordToken(username,password);
            //执行登录操作
            try{
                subject.login(token);//登录成功就返回首页
                return "index";
            }catch (UnknownAccountException e){
                model.addAttribute("msg","用户名不存在");
                return "login";
            }catch (IncorrectCredentialsException e){
                model.addAttribute("msg","密码错误");
                return "login";
            }
        }
    }

    在登录认证是会执行Realm里面的认证逻辑

    编写Realm的认证逻辑

     //执行认证逻辑
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了认证逻辑");
            String name="root";
            String password="123456";
            //将token强转为UsernamePasswordToken
            UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
            //判断用户名,不存在直接返回
            if(!token.getUsername().equals(name)){
                return  null;
            }
            //验证密码,使用AuthenticationInfo实现类SimpleAuthenticationInfo,shiro会自动验证密码
            return new SimpleAuthenticationInfo("",password,"");
        }

     整合数据库

    配置好mybatis查询逻辑

    改造Realm

     //执行认证逻辑
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了认证逻辑");
            //将token强转为UsernamePasswordToken
            UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
            User user=userService.queryUserByName(token.getUsername());
            //判断用户名,不存在直接返回
            if(user==null){
                return  null;
            }
            //验证密码,使用AuthenticationInfo实现类SimpleAuthenticationInfo,shiro会自动验证密码
            return new SimpleAuthenticationInfo("",user.getPwd(),"");
        }

    修改当前Realm的CredentialsMatcher属性可以设置加密算法

    用户授权操作

    在配置类中给资源设置权限

    filterMap.put("/user/add","perms[user:add]");

    设置没有权限时执行逻辑

    //配置未授权页面
     shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

    此时用户登录后没有访问/user/add的权限

    给用户授权,编写Realm

     //执行授权逻辑
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了授权逻辑");
            SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
            //添加资源的授权字符串
            info.addStringPermission("user:add");
            return info;
        }

    实际开发中可以在数据库增加一个字段,来获取不同用户的权限

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
               System.out.println("执行了=>授权逻辑PrincipalCollection");
               //给资源进行授权
               SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
               //添加资源的授权字符串
               //info.addStringPermission("user:add");
               Subject subject = SecurityUtils.getSubject(); //获得当前对象
               User currentUser = (User) subject.getPrincipal(); //拿到User对象
               info.addStringPermission(currentUser.getPerms()); //设置权限
               return info;
    }
  • 相关阅读:
    Flask之threading.loacl方法
    websocket之简易聊天室
    websocket介绍
    Flask之jinja2模板语言
    Flask之基础
    Linux之项目的部署
    Linux之nginx负载均衡
    Linux之nginx
    Linux之redis-cluster
    Linux之redis-sentinel
  • 原文地址:https://www.cnblogs.com/dwx-study/p/14759373.html
Copyright © 2020-2023  润新知