项目day05-shiro

一.认识shiro

  • 轻量级的权限框架
  • shiro(轻量级,粗粒度) , Spring security(细粒度)
  • RBAC:权限(登录,授权) 用户(n)-角色(n)-权限(n)(资源)

1.1 shiro的四大基石

  1. 身份认证(登录) Authentication
  2. 授权(权限) Authorization
  3. 密码学 Cryptography
  4. 会话管理 Session Management

1.2 重要的对象

二.Hello案例

建一个普通的maven项目

2.1 导包



    
    
        org.apache.shiro
        shiro-core
        1.4.0
    
    
    
        commons-logging
        commons-logging
        1.2
    
    
    
        junit
        junit
        4.9
    

2.2 ini文件

文件中有咱们的用户角色权限

# ini文件里面放的就是咱们的用户,角色,权限,资源

# -----------------------------------------------------------------------------
#  users:用户
#   root:用户名  123456:密码  admin:角色
# -----------------------------------------------------------------------------
[users]
root = 123456, admin
guest = guest, it

# -----------------------------------------------------------------------------
# roles:角色
#   admin = * :admin这个用户拥有所有权限
#   it = employee:* :it这个角色拥有员工的所有权限
#   hr = employee:save :hr这个角色拥有员工添加权限
# -----------------------------------------------------------------------------
[roles]
admin = *
it = employee:*
hr = employee:save

2.3 功能测试

主要测试登录,权限认证

@Test
public void testHello() throws Exception{
    //①.拿到权限管理对象
    /**
     * 读取了shiro.ini的文件(隐藏了realm) -> 隐藏了iniRealm
     * SecurityManager:权限管理器,shiro的所有功能都放在里面
     */
    Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    //②.相当于把SecurityManager放到了当前上下文
    /**
     * 可以让我们在当前系统的任何位置都可以拿到SecurityManager对象
     */
    SecurityUtils.setSecurityManager(securityManager);
    //③.拿到当前用户(没有登录就是游客)
    Subject currentUser = SecurityUtils.getSubject();
    System.out.println("用户是否登录:"+currentUser.isAuthenticated());

    //④.如果没有登录,让他进行登录
    if(!currentUser.isAuthenticated()){
        //ctrl+alt+t :包含代码
        try {
            //4.1 准备令牌(对象) 用户名密码令牌
            UsernamePasswordToken token = new UsernamePasswordToken("guest","guest");
            //4.2 进行登录功能
            currentUser.login(token);
        } catch (UnknownAccountException e) {
            //Unknown(未知)Account(账号)Exception:用户名不存在
            e.printStackTrace();
            System.out.println("哥,你是傻子嘛?");
        }catch (IncorrectCredentialsException e){
            //Incorrect(不正确)Credentials(凭证)Exception:密码错误
            e.printStackTrace();
            System.out.println("哥,密码错误了?");
        }catch (AuthenticationException e){
            //AuthenticationException:登录中最大的那个异常
            e.printStackTrace();
            System.out.println("发生了一个神秘的错误!!!");
        }
    }
    System.out.println("用户是否登录:"+currentUser.isAuthenticated());

    System.out.println("是否是管理员角色:"+currentUser.hasRole("admin"));
    System.out.println("是否是IT角色:"+currentUser.hasRole("it"));

    System.out.println("是否可以操作employee:save权限:"+currentUser.isPermitted("employee:save"));
    System.out.println("是否可以操作employee:index权限:"+currentUser.isPermitted("employee:index"));
    System.out.println("是否可以操作department:index权限:"+currentUser.isPermitted("department:index"));

    //⑤.还可以登出(注销)
    currentUser.logout();
    System.out.println("用户是否登录:"+currentUser.isAuthenticated());
}

三.自定义Realm

3.1 准备自定义Realm

  • 写一个Realm,继承AuthorizingRealm
  • 提供了两个方法,一个是授权doGetAuthorizationInfo,一个是身份认证 doGetAuthenticationInfo
public class MyRealm extends AuthorizingRealm {

    //授权认证功能就写在这里面
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //从数据库中获取角色并放且放到授权对象中
        Set roles = getRoles();
        authorizationInfo.setRoles(roles);
        //从数据库中获取权限并放且放到授权对象中
        Set perms = getPerms();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    /**
     * 假设这里获取到当前用户的角色
     */
    private Set getRoles(){
        Set roles = new HashSet<>();
        roles.add("admin");
        roles.add("it");
        return roles;
    }
    /**
     * 假设这里获取到当前用户的权限
     */
    private Set getPerms(){
        Set perms = new HashSet<>();
        perms.add("employee:index");
        return perms;
    }

    /**
     * 记住:如果这个方法返回null,就代表是用户名错误,shiro就会抛出:UnknownAccountException
     */
    //身份认证(登录)就写在这里面
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.拿到令牌(UsernamePasswordToken)
        UsernamePasswordToken token =  (UsernamePasswordToken)authenticationToken;
        //2.拿到用户名,判断这个用户是否存在
        // 2.1 拿到传过来的用户名
        String username = token.getUsername();
        // 2.2 根据用户名从数据库中拿到密码(以后会拿用户对象)
        String password = this.getUsers(username);
        // 2.3 如果没有拿到密码(没有通过用户名拿到相应的用户->用户不存在)
        if(password==null){
            return null;
        }

        //记住:我们只在正常完成这里的功能,shiro会判断密码是否正确
        //3.返回 AuthenticationInfo这个对象
        /**
         * 咱们创建对象需要传的参数:
         * Object principal:主体(可以乱写) -> 登录成功后,你想把哪一个对象存下来
         * Object credentials:凭证(就是密码) -> 数据库中的密码
         * credentials(密码)Salt:盐值
         * String realmName : realm的名称(可以乱写)
         */
        //拿到咱们的盐值对象(ByteSource)
        ByteSource salt = ByteSource.Util.bytes("itsource");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,"myRealm");
        return authenticationInfo;
    }

    /**
     * 假设这里是根据用户名进行的查询
     *  MD5:e10adc3949ba59abbe56e057f20f883e
     *  MD5+10次:4a95737b032e98a50c056c41f2fa9ec6
     *  MD5+10次+itsource:831d092d59f6e305ebcfa77e05135eac
     */
    public String getUsers(String username){
        if("admin".equals(username)){
            return "831d092d59f6e305ebcfa77e05135eac";
        }else if("zhang".equals(username)){
            return "123";
        }
        return null;
    }
}

3.2 测试自定义Realm

 @Test
    public void testMyRealm() throws Exception{
        //一.创建一个SecurityManager对象
        // 1.创建realm对象
        MyRealm myRealm = new MyRealm();
        // 2.创建SecurityManager对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager(myRealm);
        //②.相当于把SecurityManager放到了当前上下文
        SecurityUtils.setSecurityManager(securityManager);
        //③.拿到当前用户
        Subject subject = SecurityUtils.getSubject();

        //Hashed(哈希)Credentials(认证)Matcher(匹配器)
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //设置哈希算法
        matcher.setHashAlgorithmName("MD5");
        //设置迭代次数
        matcher.setHashIterations(10);
        //把匹配器交给shiro
        myRealm.setCredentialsMatcher(matcher);

        System.out.println("用户是否登录:"+subject.isAuthenticated());
        //④.如果没有登录,让他登录
        if(!subject.isAuthenticated()){
            try {
                UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
                subject.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("用户名错误");
            } catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                System.out.println("密码错误");
            } catch (AuthenticationException e) {
                e.printStackTrace();
                System.out.println("神迷错误");
            }
        }
        System.out.println("用户是否登录:"+subject.isAuthenticated());

        System.out.println("是否是admin角色:"+subject.hasRole("admin"));
        System.out.println("是否是hr角色:"+subject.hasRole("hr"));

        System.out.println("是否有employee:index权限:"+subject.isPermitted("employee:index"));
        System.out.println("是否有employee:save权限:"+subject.isPermitted("employee:save"));
        System.out.println("是否有department:index权限:"+subject.isPermitted("department:index"));


    }

四.集成Spring

需要有Spring的环境 ssj/项目

4.1 导包



  org.apache.shiro
  shiro-all
  1.4.0
  pom



  org.apache.shiro
  shiro-spring
  1.4.0

4.2 web.xml中配置代理过滤器



shiroFilter
org.springframework.web.filter.DelegatingFilterProxy

  targetFilterLifecycle
  true



shiroFilter
/*

4.3 准备自定义Realm

public class JpaRealm extends AuthorizingRealm {


    //授权认证功能就写在这里面
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //从数据库中获取角色并放且放到授权对象中
        Set roles = getRoles();
        authorizationInfo.setRoles(roles);
        //从数据库中获取权限并放且放到授权对象中
        Set perms = getPerms();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    /**
     * 假设这里获取到当前用户的角色
     */
    private Set getRoles(){
        Set roles = new HashSet<>();
        roles.add("admin");
        roles.add("it");
        return roles;
    }
    /**
     * 假设这里获取到当前用户的权限
     */
    private Set getPerms(){
        Set perms = new HashSet<>();
        perms.add("employee:index");
//        perms.add("user:*");
        return perms;
    }

    /**
     * 记住:如果这个方法返回null,就代表是用户名错误,shiro就会抛出:UnknownAccountException
     */
    //身份认证(登录)就写在这里面
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.拿到令牌(UsernamePasswordToken)
        UsernamePasswordToken token =  (UsernamePasswordToken)authenticationToken;
        //2.拿到用户名,判断这个用户是否存在
        // 2.1 拿到传过来的用户名
        String username = token.getUsername();
        // 2.2 根据用户名从数据库中拿到密码(以后会拿用户对象)
        String password = this.getUsers(username);
        // 2.3 如果没有拿到密码(没有通过用户名拿到相应的用户->用户不存在)
        if(password==null){
            return null;
        }

        //记住:我们只在正常完成这里的功能,shiro会判断密码是否正确
        //3.返回 AuthenticationInfo这个对象
        /**
         * 咱们创建对象需要传的参数:
         * Object principal:主体(可以乱写) -> 登录成功后,你想把哪一个对象存下来
         * Object credentials:凭证(就是密码) -> 数据库中的密码
         * credentials(密码)Salt:盐值
         * String realmName : realm的名称(可以乱写)
         */
        //拿到咱们的盐值对象(ByteSource)
        ByteSource salt = ByteSource.Util.bytes("itsource");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,"myRealm");
        return authenticationInfo;
    }

    /**
     * 假设这里是根据用户名进行的查询
     *  MD5:e10adc3949ba59abbe56e057f20f883e
     *  MD5+10次:4a95737b032e98a50c056c41f2fa9ec6
     *  MD5+10次+itsource:831d092d59f6e305ebcfa77e05135eac
     */
    public String getUsers(String username){
        if("admin".equals(username)){
            return "831d092d59f6e305ebcfa77e05135eac";
        }else if("zhang".equals(username)){
            return "123";
        }
        return null;
    }
}

4.4 准备工厂返回权限

  • 返回的Map值是有顺序的
  • 修改后要重启(热启动无效)
/**
 * 用于返回下面的这些值(这里的值是有顺序的:LinkedHashMap)
 *   
         /login = anon
         /s/permission.jsp = perms[user:index]
         /** = authc
    
    这里修改后要重新启动tomcat
 */
public class ShiroFilterMapFactory {

    public Map createMap(){
        Map map = new LinkedHashMap<>();
        //anon:需要放行的路径
        map.put("/login","anon");
        //perms:权限拦截
        map.put("/s/permission.jsp","perms[employee:index]");
        //authc:拦截
        map.put("/**","authc");
        return map;
    }
}

4.5 applicationContext-shiro.xml的配置

  • 先在 applicationContext.xml 中引入它




    
    
        
    

    
    
        
        
        
            
            
                
                
                
                
            
        
    

    
    
    
    
        
    



    
    
        
        
        
        
        
        
        
        
        
        
        
    

    
    
    
    
    


你可能感兴趣的:(JAVA)