Shiro的使用

Shiro简单使用:

 首先导入依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.9.0</version>
</dependency>

-

-

-

-

然后创建.ini文件:

  [users]

   zhanfang=123,role1               //用户名为zhanfang,密码为123, 它的角色为role1

-

-

-

[roles]

role1=user:select,user:insert          //该角色拥有的两个权限,其实就是两个字符串,这样写可读性较高, 

-

-

-

-

-

然后创建Subject对象:

IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:subject.ini");  //创建管理器工厂

SecurityManager securityManager = factory.getInstance();    //通过工厂拿到管理器对象securityManage

SecurityUtils.setSecurityManager(securityManager);  //对安全管理器对象进行全局设置

Subject subject = SecurityUtils.getSubject();  //获取用户主体

                                       //是先设置管理器对象,再获取subject主体的

-

-

-

-

然后拿到用户名和密码进行登录验证:

@RequestMapping("/subject")
@ResponseBody
public String subject(String username,String passward){
    UsernamePasswordToken token = new UsernamePasswordToken(username, passward);
        //通过用户名和密码生成token字符串
    try {
       subject.login(token);   //调用subject中的login方法和ini中的数据进行比对,从而进行登录

        boolean hasRole = subject.hasRole("role1");   //登录成功后产看是否有该角色

        System.out.println(hasRole);

       subject.checkPermission("user:select");  // 查看该角色是否有该权限,无权限则抛出异常  UnauthorizedException

       subject.isPermitted("user:select") //同样是判断该角色是否有该权限,有则值为ture, 无权限则为false

    } catch (UnauthorizedException e) {
      return "无权限";
    }catch (UnknownAccountException e){
        return "用户不存在";
    }catch (IncorrectCredentialsException e){
        return "密码错误";
    }catch (AuthenticationException e){
        return "其他错误";
    }


    return "登录成功";
}

到此shiro的简单使用就介绍完毕

-

-

-

-

shiro的加密工具:

   String  aa="slei"
   System.out.println(new  Md5Hash(aa).toHex()  )           //shiro提供了Md5Hash, 主要用来对字符串进行加密的, 通过toHex()就可以得到加密后的字符串
   new   Md5Hash(aa,"sedl")         //拼接一个新的,再进行加密
   new   Md5Hash(aa,"sedl",3)     //拼接三次,再进行加密

--------------------------

Springboot整合Shiro:

首先导入依赖:

<!--        这个是springboot整合的所有的shiro相关的依赖,也包括上面的shiro-core依赖,所有shiro-core依赖可以不用导入了-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.9.0</version>
        </dependency>

//在这个shiro-spring-boot-web-starter依赖中没有登录认证的类,所以需要你自己创建一个继承了AuthorizingRealm的bean对象,在这个bean中重写登录认证的方法,

否则就会报错"No  bean  of  type  'org.apache.shiro.reaml.Realm'   found  "

//就是必须要创建一个MyRealm类,

-

-

-

-

-

-

-

然后在application.yml中进行设置:

 shiro:

    loginUrl: /aa/subject             //访问需要登录认证的接口的时候,就会自动转跳到这个接口中,所以这个接口一般定义的是登录接口

                                 

-

-

-

-

然后创建MyRealm类:

这里面编写的是一些自定义的认证逻辑

@Component
public class MyRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

  //重写的登录方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        System.out.println("开始运行");  //只有在安全管理器中导入了这个MyRealm类,在调用subject.login(token)的时候,才会调用这里面的方法             


        String name=authenticationToken.getPrincipal().toString();     //获取用户名
                            //在运行subject.login(token)的时候,就会自动调用这个方法


        User user=userService.user(name);     //通过用户名从数据库中查询用户信息

        if(user!=null){
            AuthenticationInfo info=new SimpleAuthenticationInfo( //运行认证逻辑

                    user,     //这个参数就是一个唯一标识,在下面的权限授权方法中的principalCollection.getPrimaryPrincipal().toString() 获取的值就是这个参数值
                    user.getPassword(),           //数据库中的密码
                    ByteSource.Util.bytes("zh"),   
                                   //加入"zh"进行解密, 当初就是加入"zh"进行加密
                                  //在ShiroConfig中设置了会自动解密多少次,就是你自己创建的Shiro的配置类
                                //解密后和token中的密码进行比对,比对失败则抛出异常
                  //如果不需要解密,则可以直接不用这个参数,只使用三个参数就可以了


                  authenticationToken.getPrincipal().toString()   //当前用户名称
            );
            return info;             //拿到用户信息
        }

        return null;
    }


    //重写的授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                principalCollection.getPrimaryPrincipal().toString()            //这个是获取到登录认证中的第一个参数的值
        return null;
    }
}

-

-

-

-

-

然后创建两个使用Shiro必备的配置类,安全管理器和授权对象:

@Configuration
public class
ShiroConfig {
    @Resource
    private MyRealm myRealm;

//安全管理器,它其实就是下面的SecurityUtils默认的一个管理器对象
//所以在下面获取subject对象之前,就不用像上面那样通过SecurityUtils.setSecuritymanage()重新设置管理器对象了,这一点是比较方便的
    @Bean
    public DefaultWebSecurityManager  defaultWebSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();

        HashedCredentialsMatcher  matcher=new HashedCredentialsMatcher();   //创建解密对象

        matcher.setHashAlgorithmName("md5");   //使用md5的解密方式

        matcher.setHashIterations(3);       //在登录认证的时候,设置MyRealm中的解密,会自动解密三次

        myRealm.setCredentialsMatcher(matcher);    //将解密对象导入到MyRealm中

        defaultWebSecurityManager.setRealm(myRealm);  //导入自定义的登录认证的类MyRealm

        return  defaultWebSecurityManager;
    }


//授权对象
    @Bean
    public DefaultShiroFilterChainDefinition  defaultShiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition   dfc=new DefaultShiroFilterChainDefinition();

        dfc.addPathDefinition("/aa/bb","anon");   //访问这个接口无需登录认证, anon表示无需登录认证

        dfc.addPathDefinition("/aa/cc","authc");   //访问这个接口需要登录认证, anon表示需要登录认证

        return dfc;
    }

}

-

-

-

-

-

-

最后编写认证代码:

@Controller
@RequestMapping("/aa")
public class AAController {
   @RequestMapping("/subject")
   @ResponseBody
   public String subject(String username,String passward){

    UsernamePasswordToken token = new UsernamePasswordToken(username, passward);
    Subject subject= SecurityUtils.getSubject(); //这里是直接调用获取subject对象就行
               //不需要像之前一样将安全管理器设置到SecurityUtils中了
            
    try {
       subject.login(token);    //它会自动调用MyRealm中的认证方法
        boolean hasRole = subject.hasRole("role");
        System.out.println(hasRole);

       subject.checkPermission("user:select");

    } catch (UnauthorizedException e) {
      return "无权限";
    }catch (UnknownAccountException e){
        return "用户不存在";
    }catch (IncorrectCredentialsException e){
        return "密码错误";
    }catch (AuthenticationException e){
        return "其他错误";
    }


    return "登录成功";
}

}

总结:Springboot中使用Shiro的步骤:

        第一:导入依赖

       第二:设置application.yml

       第三:创建MyRealm

       第四:创建两个配置类,前者用来配置MyRealm类,后者用来设置拦截接口

      第五:编写认证方法

注意:如果数据库中的密码是加密后的密码,并且加密了多次:

              比如:  new   Md5Hash("zhan","zh",3)  ,这里是加密后通过”zh"加密了三次

        那么在配置类中的安全管理器中的给MyRealm中传入的解密对象,也要设置对应的解密次数

       比如:    matcher.setHashIterations(3);           //解密三次

                  myRealm.setCredentialsMatcher(matcher); //将解密对象导入到MyRealm中

----------------------------

在安全管理器这个配置类中配置多个MyRealm对象:

就是ShiroConfig中必备的两个配置类中的其中一个配置类

   

//默认安全管理器对象
    @Bean
    public DefaultWebSecurityManager  defaultWebSecurityManager(){
1,-------------
        //创建安全管理器对象
        DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();

2,--------------
        //设置认证策略
        ModularRealmAuthenticator   mra=new ModularRealmAuthenticator();          //创建认证对象

      AtLeastOneSuccessfulStrategy   als1=new AtLeastOneSuccessfulStrategy();   //创建认证策略,三个中,只要一个认证成功,就通过认证
      //这个是默认的策略,其实可以不用设置

//        mra.setAuthenticationStrategy(als);          //给认证对象设置了这个认证策略
//
//        defaultWebSecurityManager.setAuthenticator(mra);        //安全管理器中传入对应认证策略的对象


3,-------------
        //给指定MyRealm设置解密对象
        HashedCredentialsMatcher  matcher=new HashedCredentialsMatcher();   //创建解密对象
        matcher.setHashAlgorithmName("md5");   //使用md5的解密方式
        matcher.setHashIterations(1);       //在登录认证的时候,设置MyRealm中的解密,会自动解密一次
        myRealm.setCredentialsMatcher(matcher);    //将解密对象导入到MyRealm中


4,------------
        //将三个MyRealm认证对象放入集合中
        List  list=new ArrayList();
        list.add(myRealm);
        list.add(myRealm2);
        list.add(myRealm3);


        defaultWebSecurityManager.setRealms(list);        //通过setRealms可以导入多个MyRealm对象

        return  defaultWebSecurityManager;
    }

总结:

 在Shiro中定义了三种认证策略
             a) AtLeastOneSuccessfulStrategy:    只要有任意一个Realm验证成功,那么认证将视为成功, 一般都是使用这种策略,这种策略也是默认的策略
                 它将返回所有MyRealm身份认证成功的认证信息
                     比如:
                         这种策略它是三个MyRealm都会运行, 如果一个都没成功,则报"其他错误"


             b) FirstSuccessfulStrategy: 只要有一个Realm验证成功,整体认证将是为成功,
                  它将返回第一个身份认证成功的认证信息,其他都将忽略
                        比如: 三个MyRealm都会运行,如果一个都没成功,则报"其他错误"
                  


             c) AllSuccessfulStrategy: 所有Realm成功,认证才视为成功
                    比如:
                         它会先运行第一个MyRealm, 成功后,才会运行第二个MyRealm
                         第二个MyRealm成功后,才会运行第三个MyRealm
                         只有三个MyRealm都认证成功后,才算是真正的认证成功

   

注意: 在上面的四步中 ,如果使用默认的策略,则第二步的设置认证策略可以不用写,这样就整体简便了很多

--------------------------

在shiro中设置RememberMe功能:

首先在shiro的两大配置类中进行配置: 

在ShiroConfig中

@Configuration

public class ShiroConfig {   

@Bean
public DefaultWebSecurityManager  defaultWebSecurityManager(){
    //创建安全管理器对象
DefaultWebSecurityManager defaultWebSecurityManager=new   DefaultWebSecurityManager();
  //创建shiro专用的cookie对象
    SimpleCookie  cookie=new SimpleCookie("rememberMe");     
    cookie.setPath("/");
    cookie.setMaxAge(100);       //设置100秒


//创建Cookie管理器
    CookieRememberMeManager  cm=new CookieRememberMeManager();   
    cm.setCookie(cookie);        //将专用cookie放入cookie管理器中
    cm.setCipherKey("1234567898765432".getBytes());          //设置cookie的加密密钥,这样才能生效


//将cookie管理器放入安全管理器中
    defaultWebSecurityManager.setRememberMeManager(cm);     



    return  defaultWebSecurityManager;
}

    //shiro拦截器
    @Bean
    public DefaultShiroFilterChainDefinition  defaultShiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition   dfc=new DefaultShiroFilterChainDefinition();    //设置拦截器

        dfc.addPathDefinition("/aa/w","anon");   //访问这些接口不需要登录认证, anon表示无需认证
        dfc.addPathDefinition("/aa/subject","anon");

        dfc.addPathDefinition("/aa/w","authc");  //访问这些接口需要登录认证, anon表示需要认证

        dfc.addPathDefinition("/logout","logout");  //退出接口

        dfc.addPathDefinition("/aa/w","user");   //点击RememberMe功能后,只要登录一次,在后续的100秒就记住了你,不需要再次登录,就能进行访问
                                                //只有点击RememberMe功能,该拦截器才会起作用

        return dfc;
    }

}

拦截器的优先级:”user">"authc">"anon"

比如: 三个拦截器都拦截/aa/w接口

          当访问/aa/w的时候,被拦截进行登录认证, 从这可以看出 "需登录认证(authc) " >  "不需登录认证(anon) "

         当在登录的时候,点击rememberMe的时候,你退出页面再次访问,会发现不需要重新进行登录认证,从这可以看出 "rememberMe功能的拦截器 (user) ">  “需登录认证(authc) "

-

-

-

然后编写登录逻辑:

 subject.login(username,password, rememberMe)            //这里是前端传入用户名,密码,以及布尔值,当rememberMe为true的时候,就表示开启rememberMe功能

                                                              //在开启rememberMe功能并登录成功后,浏览器会得到一个名称为“rememberMe” 的 cookie对象

-

-

-

登录时开启RememberMe功能时报错:

当你开启RememberMe功能的时候,会出现报错"SerializationException", 这是因为Shiro框架在执行"RememberMe"功能时,会将登录认证中查询到的数据库中的用户信息User进行序列化,如果User这个实体类没有继承序列化接口"Serializable",那么就会报错

解决方法就是: public   class   User   implements   Serializable{              //这个User实体类是用来接受从数据库中查询的用户的信息的

 

                                      }

---------------------

在shiro中实现退出功能:

一,使用shiro拦截器进行退出操作

 @Bean

public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition(){

                DefaultShiroFilterChainDefinition dfc=new DefaultShiroFilterChainDefinition();                //设置拦截器

                dfc.addPathDefinition("/logout","logout");        //退出接口,/logout这个接口是不存在的,理论上拦截到/logout接口的时候,就会自动执行拦截器中的退出操作

                                                                   //但是不知道为什么使用这种方法是无法正常退出的,总是失败报404,所以还是不要用了

                                  }

-

-

-

-

-

-

二,自定义Controller进行退出操作

@RequestMapping("/logout")
@ResponseBody
public String logout(){
    Subject subject = SecurityUtils.getSubject();          //获取subject主体
    subject.logout();           //进行退出
    return "退出成功";
}

 

-----------------

授权和角色认证:

首先在shiro框架中设置权限和角色:

在MyRealm中:

@Component
public class MyRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

  //重写的登录方法
    @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

          }

 //重写的授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String  name=principalCollection.getPrimaryPrincipal().toString();        //获取用户名称

        List<String >  role=userService.getRoleInfo(name);       //从数据库中查询用户的所有角色  

      List<String>  permiseion = null;
         if(role.size()>0){         //判断是否有角色,权限是来自与角色的,没有角色就不用查权限了
             permiseion=userService.getPermisionInfo(role);   //获取给用户所有权限
                   }


      SimpleAuthorizationInfo  info=new SimpleAuthorizationInfo();
           info.addRoles(role);     //添加多个角色

       if(permiseion!=null){     //判断是否有权限
              info.addStringPermissions(permiseion);      //添加多个权限
         }
        info.addRole("aa");      //添加一个角色
        info.addStringPermiseion("user:add")       //添加一个权限

        return info;    //这是最重要的, 每当进行权限和角色判断,都是在这个info对象中进行比对的
    }
}

-

-

-

-

-

然后使用注解给接口设置访问权限:

 

@RestController
@RequestMapping("/roles")
public class RolesController {
    @RequiresRoles("admin")          //角色验证
   @RequiresPermissions("user:select")         //权限认证
    @RequestMapping("/aa")
    public String  roles(){
        return "验证角色和权限成功成功";
    }

}

-

-

-

-

然后设置异常处理器处理异常:

@RestControllerAdvice
public class ProjectAdvice {

    @ExceptionHandler(Exception.class)
    public String Exception(Exception e){
        return "出现异常";
    }
@ExceptionHandler(UnauthorizedException.class)
public String  aa(UnauthorizedException e){
    return e.getMessage();       //当没有权限和没有角色都是抛出这个异常的
                       //没角色时打印结果为:Subject does not have role [admin]
                       //没权限时打印结果为:Subject does not have permission [user:select]
                       
}
}

---------------------

shiro权限页面

  首先导入依赖:

<dependency>       //这是thymeleaf整合shiro的一个依赖
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

-

-

-

然后编写配置

@Configuration
public class  ShiroConfig {
    @Bean
    public ShiroDialect  shiroDialect(){
        return new ShiroDialect();         //主要用来解析thymeleaf中的shiro:相关的属性
    }

}

-

-

-

-

然后在动态页面shiro.html中:     注意:一定要在templates中的页面中才可以使用shiro, 因为只有这里面的页面才会被thymeleaf引擎进行加载

<body>   

//是否拥有指定角色

<h1 shiro:lacksrole="admin">没角色admin才会显示</h1>    //这是第一种写法,直接将shiro:hasRole当成属性来用
<h1 shiro:hasrole="admin">有角色admin才会显示</h1> 
<shiro:hasRole  name="admin">有角色admin才会显示 </shiro:hasRole>          //这是第二种写法,直接将shiro:hasRole当成标签类使用, 是不区分大小写的,shiro:hasrole也可以

    

// 是否登录

<shiro:guest>游客</shiro:guest>      //未执行subject.login()的时候才会显示
<shiro:user>成功登录</shiro:user>    //执行了subject.login()才会显示

 //是否拥有指定权限

<shiro:lacksPermission name="user:add">没有权限user:add才会显示</shiro:lacksPermission>
<shiro:hasPermission name="user:add">拥有权限user:add才会显示</shiro:hasPermission>

 

</body>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值