你还不会shiro吗?

  • 前奏

  • shiro核心配置文件(rolesFilter可选)。

  • 身份认证

  • 多表登录源如何操作?

  • 授权管理

  • 如何解决界面多角色/资源问题

  • 访问效果


权限管理在日常开发中很重要,所以硬着头皮也要啃下来。

实现功能:

  • 身份认证

  • 对不同页面进行url授权

  • 多表登录解决

  • 同一个页面多role访问

项目完整github地址 欢迎star

springboot一些学习整合完整地址

shiro的四大组件:

  • 身份认证(Authentication

    )-证明用户身份,通常叫做登陆(login)。

  • 授权(Authorization)

    -访问控制

  • 加密(Cryptography)-保护或隐藏数据

  • 会话管理(session management)每个用户时间敏感状态

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

  • Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
      Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

  • SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
      从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

  • Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

Shiro内置过滤器,可以实现权限相关的拦截器

  • 常用的过滤器:

  • anon: 无需认证(登录)可以访问

  • authc: 必须认证才可以访问

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

  • perm: 该资源必须得到资源权限才可以访问

  • role: 该资源必须得到角色权限才可以访问

这里面只用到了身份认证和授权,权限认证只用到了一点点,shiro的原理是封装的过滤器,他能够在访问浏览器前能过自动完成一些内容。

shiro配置主要两部分——shiroconfig和自定义的Realm(继承AuthorizingRealm)。其中,shiroconfig是shiro的主要配置文件,而自定义的Realm主要是重写AuthorizingRealm的两个方法,分别是身份认证和授权认证调用数据库查询比对。而如果需要role访问则需要重写一个filter。

前奏

项目结构:

环境:

  • Springboot2

  • mybatis

  • shiro

新建表:

对应的bean:

package com.shiro.bean;

public class student {
   private String username;
   private String password;
   private String role;
   private String perm;
  //省略get set

mybatis简单查询:

package com.shiro.mapper;


import com.shiro.bean.student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface studentMapper {

   @Select("select  * from student where username=#{name}")
   student findByName(String name);
}

省略html和sql,详细可以到GitHub下载

页面目录,:

shiro核心配置文件(rolesFilter可选)。

UserRealm.java

package com.shiro.config;

import com.shiro.bean.student;
import com.shiro.mapper.studentMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


/**
* 自定义Realm
* @author bigsai
*
*/

public class UserRealm extends AuthorizingRealm{

@Autowired(required = false)
private studentMapper studentMapper;
private final Logger logger= LoggerFactory.getLogger(UserRealm.class);
/**
* 执行授权逻辑
*/

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
logger.info("执行逻辑授权");

//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//添加资源的授权字符串
//到数据库查询当前登录用户的授权字符串
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
student user = (student) subject.getPrincipal();
student dbUser = studentMapper.findByName(user.getUsername());

info.addRole(user.getRole());//添加role 和perms  role代表角色 perms代表操作,或者动作等。用于颗粒化权限管理
info.addStringPermission(dbUser.getPerm());
System.out.println("user:"+dbUser.getPerm());
return info;
}
/**
* 执行认证逻辑
*/

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;

student user = studentMapper.findByName(token.getUsername());

if(user==null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}

//2.判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}

}

rolesFilter

package com.shiro.config;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// AuthorizationFilter抽象类事项了javax.servlet.Filter接口,它是个过滤器。
public class rolesFilter extends AuthorizationFilter {

   @Override
   protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
       Subject subject = getSubject(req, resp);
       String[] rolesArray = (String[]) mappedValue;

       if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
           return true;
       }
       for (int i = 0; i < rolesArray.length; i++) {
           if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
               return true;
           }
       }
       return false;
   }
}

shiroConfig:shiro的主要配置

package com.shiro.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;


/**
* Shiro的配置类
*
* @author bigsai
*/

@Configuration
public class ShiroConfig {

   /**
    * 创建ShiroFilterFactoryBean
    */

   @Bean
   public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
       ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

       //设置安全管理器
       shiroFilterFactoryBean.setSecurityManager(securityManager);


       Map<String, Filter> filtersMap = new LinkedHashMap<>();
       filtersMap.put("rolesFilter",new rolesFilter());
       shiroFilterFactoryBean.setFilters(filtersMap);//使用自定义fitter
       //添加Shiro内置过滤器
       /**
        * Shiro内置过滤器,可以实现权限相关的拦截器
        *    常用的过滤器:
        *       anon: 无需认证(登录)可以访问
        *       authc: 必须认证才可以访问
        *       user: 如果使用rememberMe的功能可以直接访问
        *       perm: 该资源必须得到资源权限才可以访问
        *       role: 该资源必须得到角色权限才可以访问
        */

       Map<String, String> filterMap = new LinkedHashMap<String, String>();



       filterMap.put("/login", "anon");//要将登陆的接口放出来,不然没权限访问登陆的接口
       filterMap.put("/getcontroller", "anon");
//
       //授权过滤器
       //注意:当前授权拦截后,shiro会自动跳转到未授权页面
       filterMap.put("/add", "perms[add]");
       filterMap.put("/update", "perms[update]");

//
       filterMap.put("/test1.html","rolesFilter[admin,user]");
       filterMap.put("/*", "authc");//authc即为认证登陆后即可访问

       
       //修改调整的登录页面
       shiroFilterFactoryBean.setLoginUrl("/index");
       //设置未授权提示页面
       shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

       shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


       return shiroFilterFactoryBean;
   }

   /**
    * 创建DefaultWebSecurityManager
    */

   @Bean(name = "securityManager")
   public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
       DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
       //关联realm
       securityManager.setRealm(userRealm);
       return securityManager;
   }

   /**
    * 创建Realm
    */

   @Bean(name = "userRealm")
   public UserRealm getRealm() {
       return new UserRealm();
   }

}

身份认证

身份认证,就是登录校检。这是第一层过滤,并且当用户没有登录的时候,回退到没登陆的界面。在controller中,login的核心为:

 @RequestMapping("/login")
   public String login(String name, String password, Model model, HttpServletRequest request) {

       model.addAttribute("nama", "给个star");
       /**
        * 使用Shiro编写认证操作
        */

       //1.获取Subject
       Subject subject = SecurityUtils.getSubject();

       //2.封装用户数据
       UsernamePasswordToken token = new UsernamePasswordToken(name, password);

       //3.执行登录方法
       try {
           subject.login(token);

           //登录成功
           //跳转
           return "redirect:/index2";
       } catch (UnknownAccountException e) {
           //e.printStackTrace();
           //登录失败:用户名不存在
           model.addAttribute("msg", "用户名不存在");
           return "login";
       } catch (IncorrectCredentialsException e) {
           //e.printStackTrace();
           //登录失败:密码错误
           model.addAttribute("msg", "密码错误");
           return "login";
       }
   }

releam中

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;

student user = studentMapper.findByName(token.getUsername());
if(user==null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}

而这只是表象的处理过程,而在releam(继承AuthorizingRealm)中需要充血doGetAuthenticationInfo()方法.

大致流程为:登录——>拿账号密码检验———>用着token的账号通过你的sql查询对象——>比对数据是否一致——>通过还是抛各种异常

而在shiroConfig中,基于url过滤时authc即可访问

多表登录源如何操作?

可能会遇到如下情况:教师端,学生端来自两张表,两个登录接口,我该如何使用shiro身份认证。对于这种问题,你可以配置多个releam,但是我觉得如果简单你可以在不同的登录接口下传递一个参数过来,这个参数就用session传递。因为,shiro的session和网页httprequest获得的session是同一个session

所以当你在login传递一个属性到releam中,可用 if else判断然后不同登录接口执行不同的查询方法即可。

授权管理

接上流程
是否登录——>是/否——(是)—>查询role/perm添加到subject——>过滤器校验该url需要权限——>可以访问/权限不足

shiro主要url可以根据角色(role)和资源(perm)的管理。对于role,可以是管理员,教师等,而perm,可能是一个动作,一个操作,等等。并且可能一个角色拥有多个role和perm。
同理,授权就是查询数据库的role或者perm字段添加到角色中。当然具体api不做介绍。
主要方法为上述:

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
logger.info("执行逻辑授权");

//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//添加资源的授权字符串
//到数据库查询当前登录用户的授权字符串
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
student user = (student) subject.getPrincipal();
student dbUser = studentMapper.findByName(user.getUsername());

info.addRole(user.getRole());//添加role 和perms  role代表角色 perms代表操作,或者动作等。用于颗粒化权限管理
info.addStringPermission(dbUser.getPerm());
System.out.println("user:"+dbUser.getPerm());
return info;
}

而url中也是

 filterMap.put("/add", "perms[add]");
filterMap.put("/update", "roles[admin]");

如何解决界面多角色/资源问题

常常遇到这种情况:一个接口/页面,有两个或者以上角色可以访问。然后再后台的过滤器配置总。shiro默认的配置是and而不是or。这就需要我们自己定义filter继承AuthorizationFilter从写对应方法。

以多角色访问为例子。从写上述就是文件rolesFilter。在使用的时候也要首先声明filter才能使用。

访问效果

在页面授权的

运行测试:访问其他接口都被返回到这个界面

登陆成功后,界面可以访问





有个小注意点:如果mybatis2.0版本回和spring-start-web有冲突。我用1.3.2版本没问题。
参考:百度百科
项目github地址

springboot一些学习整合完整地址


©著作权归作者所有:来自51CTO博客作者MySQL技术栈的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 使用Shiro加密与解密实现简单用户注册与登录验证
  2. [Android实例] android登录Web以及登录保持,cookie管理相关
  3. 【Android】 使用okhttp实现注册登录功能(与服务器端)
  4. 新应用的知识整理-1-启动页
  5. android用SharedPreferences保存登录账户密码
  6. Android判断是否首次登录
  7. Android支付宝-2016最新版支付宝,实现支付功能
  8. 安卓在子线程中使用使用Toast
  9. 友盟三方登录,分享,推送demo

随机推荐

  1. element.replaceWith在自定义指令的链接
  2. JavaScript基础知识(二)
  3. electron 将pc端(vue)页面打包为桌面端应用
  4. 系列之前端:从重复造轮子说起
  5. 使用一个CSS选择网页外观而无需重新加载
  6. 具有相同名称的Mutiple按钮显示不同的div
  7. 在聚焦输入时,在iOS(和所有移动设备)上自定
  8. 使用bootstrap模式框的自定义选择框错误
  9. 《疯狂html5/css3/javascript讲义》这本
  10. 如果外部应用程序更改了持久模型(服务器