shiro安全框架的介绍和使用配置详情

介绍:

Shiro是apache旗下一个开源安全框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。

概要架构:

详细架构:

第一层: subject 

第二层: securiryManager  --  Shiro 的核心,用来协调管理组件工作

Authenticator: 认证管理器  -- 负责执行认证操作

Authorizer: 授权管理器  -- 负责授权检测

Session manage :

第三层:realm  -- Realm 是 shiro 和你的应用程序安全数据之间的桥梁。

实战:

配置Shiro安全过滤器

WEB-INF/web.xml

  <!-- 配置Shiro安全过滤器 -->
  <filter>
		<filter-name>DelegatingFilterProxy</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 初始化参数 -->
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>DelegatingFilterProxy</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

spring-configs.xml

    <!-- 配置shiro框架-->
    
    <!-- 配置realm对象(将给spring管理) -->
    <bean id="userRealm" 
            class="com.jt.sys.service.realm.ShiroUserRealm">
        <!-- 凭证匹配器(密码加密) -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"/>
                <!-- <property name="hashIterations" value="1024"/> -->
            </bean>
        </property>
    </bean>
    <!-- 配置CacheManager对象(不是必须的,主要是为了提高性能,可以对认证信息以及授权信息进行缓存) -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
             will be creaed with a default config:
             <property name="cacheManager" ref="ehCacheManager"/> -->
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.: -->
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
    </bean>
    <!-- 配置securityManager对象(此对象时shiro框架核心) -->
    <bean id="securityManager" 
       class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/> 
        <property name="cacheManager" ref="cacheManager"/>  
    </bean>
    <!-- 配置ShiroFilter(通过此filter的配置实现对请求资源的过滤,哪些请求要放行,哪些要认证)-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
         <!-- shiro的核心安全接口 -->
         <property name="securityManager" ref="securityManager"/>
         <!-- 要求登录时的连接 -->
         <property name="loginUrl" value="/loginUI.do"></property>
         <!-- 登录成功后要跳转的连接(此处已经在登录中处理了) -->
         <!-- <property name="successUrl" value="/index.jsp"></property> -->
         <!-- 访问未对其授权的资源时,要跳转的连接 
         <property name="unauthorizedUrl" value="/default.html"></property>-->
         <!-- shiro连接约束配置 -->
         <property name="filterChainDefinitions">
             <value>
                 <!-- 对静态资源设置允许匿名访问 -->
                 /bower_components/** = anon
                 /build/** = anon
                 /dist/** = anon
                 /plugins/** = anon
                 /doLogin.do = anon
                 <!-- 退出 -->
                 /doLogout.do = logout  <!-- 会调用Subject的logout方法,此方法会将session清空 -->
                 <!-- 剩余其他路径,必须认证通过才可以访问 -->
                 /** = authc
             </value>
         </property>
     </bean>
     
    <!--Shiro生命周期处理器-->
    <bean id="lifecycleBeanPostProcessor" 
    class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!--启用shiro注解权限检查(@RequestPermissions)-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor"/>
    <bean  class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" 
                  ref="securityManager"/>
    </bean>

认证:

认证流程:

 

  1. 系统调用subject的login方法将用户信息token提交给SecurityManager

          subject.login(token)

        //此请求会提交给SecurityManager

        //SecurityManager会调用认证处理器Authenticator

        //认证处理器会去访问相关Realm对象获取认证信息

  1. SecurityManager将认证操作委托给认证器对象Authenticator
  2. Authenticator将身份信息传递给Realm。

SysUser sysUser = sysUserDao.findUserByUserName(username);

  1. Realm访问数据库获取用户信息然后对信息进行封装并返回。

        //3.2 对用户信息进行封装

        AuthenticationInfo info

        = new SimpleAuthenticationInfo(

                sysUser.getUsername(), //主身份

                sysUser.getPassword(), //已经加密的密码

                byteSource,  //salt对应的字节源对象

                getName()); //realm的名字

5)Authenticator 对realm返回的信息进行身份认证。

 

SysLoginController.java

@Controller
@RequestMapping("/")
public class SysLoginController {
	  @Autowired
      private SysUserService sysUserService;
	  @RequestMapping("loginUI")
	  public String loginUI(){
		  return "login";
	  }
	  @RequestMapping("doLogin")
	  @ResponseBody
	  public JsonResult doLogin(String username, String password){
		  String r=DigestUtils.md5DigestAsHex("123456".getBytes());
		  System.out.println("r="+r);
		    sysUserService.login(username, password);
		  return new JsonResult("login ok");
	  }
}
@Service
public class SysUserServiceImpl implements SysUserService {
	
	@Autowired
	private SysUserDao sysUserDao;
	@Autowired
	private SysRoleDao sysRoleDao;
	@Autowired
	private SysUserRoleDao sysUserRoleDao;
	
	@Override
	public void login(String username,String password) {
		System.out.println("service.login");
		//0.参数合法性验证
		if(StringUtils.isEmpty(username))
		throw new ServiceException("用户名不能为空");
		if(StringUtils.isEmpty(password))
		throw new ServiceException("密码不能为空");
		//1.获取Subject(主体)对象
		Subject subject=SecurityUtils.getSubject();
		//2.封装用户名和密码
		UsernamePasswordToken token=new UsernamePasswordToken(username, password);
	    //3.执行身份认证
		try {
		subject.login(token);
		//此请求会提交给SecurityManager
		//SecurityManager会调用认证处理器Authenticator
		//认证处理器会去访问相关Realm对象获取认证信息
		} catch (AuthenticationException e) {
		e.printStackTrace();
		throw new ServiceException("用户名或密码不正确");
		}
		//4.记录用户信息
		Session session=
		SecurityUtils.getSubject().getSession();
	    session.setAttribute("user", username);
	}

继承AuthorizingRealm ,重写doGetAuthenticationInfo

通过Realm实现基本认证及权限控制

public class ShiroUserRealm extends AuthorizingRealm {

	@Autowired
	private SysUserDao sysUserDao;

	/**
	 * 完成认证信息的获取以及封装
	 * 此方法何时调用?(执行登陆认证时调用)
	 * @param  
	 * 用于接收用户身份以及凭证信息的对象(用户输入的)
	 * 
	 * @return AuthenticationInfo  
	 * 封装了认证信息的对象(从数据库查询到的)
	 * 
	 * client-->controller-->service-->realm
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("realm.doGetAuthenticationInfo");
		//1.获取用户身份信息
		UsernamePasswordToken uToken= 	(UsernamePasswordToken)token;
		String username=uToken.getUsername();
		//2.基于用户身份查询数据库信息
		SysUser sysUser=
		sysUserDao.findUserByUserName(username);
		//3.对查询结果进行封装.
		//3.1获取用户salt值,并将其转换为一个字节源对象
		ByteSource byteSource= ByteSource.Util.bytes(sysUser.getSalt());
		//3.2对用户信息进行封装返回.
		AuthenticationInfo info=
		new SimpleAuthenticationInfo(
		sysUser.getUsername(), //主身份
		    sysUser.getPassword(), //已加密的密码
		    byteSource,//salt对应的字节源对象
		    getName());//realm 的名字
		return info;
	}
}
public interface SysUserDao {
	/**
	 * 根据用户名查找用户信息
	 * @param username
	 * @return
	 */
	SysUser findUserByUserName(String username);
}
     <select id="findUserByUserName" resultType="sysUser">
           select *  from sys_users
           where username=#{username}        
     </select>

授权实现:

流程:

  1. 系统调用subject相关方法isPermitted基于资源/hasRole将用户信息递交给SecurityManager

2)SecurityManager将权限检测操作委托给Authorizer对象

3)Authorizer将用户信息委托给realm. 

 List<String> list = sysUserDao.findUserPermissions(username);

  1. Realm访问数据库获取用户权限信息并封装

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

info.setStringPermissions(permissions);

  1. Authorizer对用户授权信息进行判定。

@RequiresPermissions("sys:user:update")

    @RequestMapping("doDeleteObject")
    @ResponseBody
    public JsonResult doDeleteObject(String idStr){
    	sysRoleService.deleteObject(idStr);
    	return new JsonResult();
    }
	@RequiresPermissions("sys:role:delete")
	@Override
	public int deleteObject(String idStr) {
		//1.参数合法性验证
		if(StringUtils.isEmpty(idStr))
		throw new ServiceException("必须选中才能删除");
		//2.解析字符串
		String[] ids=idStr.split(",");
		//3.调用数据层方法执行删除操作
		int rows=sysRoleDao.deleteObject(ids);
		
		for(String id:ids){
			sysRoleMenuDao.deleteObject(Integer.valueOf(id));
		    sysUserRoleDao.deleteObject(null, Integer.valueOf(id));
		}
		//4.返回处理结果
		if(rows==0)
		throw new ServiceException("数据已经不存在");
		return rows;
	}

通过Realm实现基本认证及权限控制

public class ShiroUserRealm extends AuthorizingRealm {
	
	@Autowired
	private SysUserDao sysUserDao;
	/***
	 * 完成授权信息的获取以及封装.
	 * 此方法何时调用?(执行授权检测时调用)
	 * 
	 */
	@Override
	protected AuthorizationInfo 
	doGetAuthorizationInfo(
			PrincipalCollection principals) {
		System.out.println("realm.doGetAuthorizationInfo");
		//1.获取登陆用户身份信息
		String username=
		(String)principals.getPrimaryPrincipal();
		//2.查找用户的权限信息
		List<String> list=//sys:user:update,sys:user:view,....,
		sysUserDao.findUserPermissions(username);
		System.out.println("list="+list);
		Set<String> permissions=new HashSet<>();
		for(String permission:list){
			if(!StringUtils.isEmpty(permission)){
				permissions.add(permission);
			}
		}
		
		System.out.println("set="+permissions);
		//3.对权限信息进行封装
		SimpleAuthorizationInfo info=
		new SimpleAuthorizationInfo();
		info.setStringPermissions(permissions);
		return info;
	}
	/**
	 * 根据用户id查找用户权限标识信息
	 * 例如:sys:role:view,sys:role:add
	 * @param userId
	 * @return
	 */
	List<String> findUserPermissions(String username);
     <select id="findUserPermissions"
             resultType="string">
           select m.permission
           from sys_users u join 
                sys_user_roles ur join 
                sys_role_menus rm join 
                sys_menus m
                
                on u.id=ur.user_id   and 
                ur.role_id=rm.role_id  and 
                rm.menu_id=m.id 
           where u.username=#{username}
     </select>
 <!-- 整合Shiro 安全框架-->
 
  <dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.3.2</version>
  </dependency>

相关表:

sys_roles

sys_users

sys_menus

sys_role_menus

sys_user_roles

CREATE TABLE `sys_roles` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL COMMENT '角色名称',
  `note` varchar(500) DEFAULT NULL COMMENT '备注',
  `createdTime` datetime DEFAULT NULL COMMENT '创建时间',
  `modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
  `createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
  `modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COMMENT='角色';
CREATE TABLE `sys_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `salt` varchar(50) DEFAULT NULL COMMENT '盐  密码加密时前缀,使加密后的值不同',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
  `valid` tinyint(4) DEFAULT NULL COMMENT '状态  0:禁用   1:正常  默认值 :1',
  `createdTime` datetime DEFAULT NULL COMMENT '创建时间',
  `modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
  `createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
  `modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='系统用户';
CREATE TABLE `sys_menus` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL COMMENT '资源名称',
  `url` varchar(200) DEFAULT NULL COMMENT '资源URL',
  `type` int(11) DEFAULT NULL COMMENT '类型     1:菜单   2:按钮',
  `sort` int(11) DEFAULT NULL COMMENT '排序',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  `parentId` int(11) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
  `permission` varchar(500) DEFAULT NULL COMMENT '授权(如:user:create)',
  `createdTime` datetime DEFAULT NULL COMMENT '创建时间',
  `modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
  `createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
  `modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=141 DEFAULT CHARSET=utf8 COMMENT='资源管理';
CREATE TABLE `sys_role_menus` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色ID',
  `menu_id` int(11) DEFAULT NULL COMMENT 'ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1218 DEFAULT CHARSET=utf8 COMMENT='角色与菜单对应关系';
CREATE TABLE `sys_user_roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `role_id` int(11) DEFAULT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';
INSERT INTO `jt_sys`.`sys_users`(`id`, `username`, `password`, `salt`, `email`, `mobile`, `valid`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (1, 'admin', '4ebd394fbd25e495e0753a7dc9889a8e', '7adb778c-e7d3-4dd3-a3c5-5f80a158006d', '[email protected]', '13624356789', 1, NULL, '2018-01-13 02:06:45', NULL, 'admin');
 
INSERT INTO `jt_sys`.`sys_menus`(`id`, `name`, `url`, `type`, `sort`, `note`, `parentId`, `permission`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (45, '用户管理', 'user/listUI.do', 1, 45, NULL, 8, 'sys:user:view', '2017-07-12 15:15:59', '2017-07-21 17:36:01', 'admin', 'admin');
 

猜你喜欢

转载自blog.csdn.net/qq_24271537/article/details/109324820