shiro入门

概 述

shiro框架是apache旗下的一个开源安全框架,他将软件系统的安全认证相关功能抽取出来,实现用户身份认证,权限授权,加密,会话管理等功能,组成了一个通用的安全认证框架。

Shiro架构

shiro3个核心组件

shiro入门_第1张图片

  • subject:主体对象,负责提交用户认证和授权信息,与软件交互的一个特定的实体。
  • SecurityManager:安全管理器,负责认证,授权等业务的实现。
  • Realm:领域对象,是shiro和应用程序数据交互的桥梁。

shiro详细架构

shiro入门_第2张图片

  • 1)subject(主体):当前与软件进行交互的实体(用户,第三方服务,计划任务等)。
  • 2)Securitymanager(安全管理器):shiro的核心,用于协调管理组件的工作。
  • 3)Authenticator(认证管理器):负责进行认证操作。
  • 4)Authorizer(授权管理器):负责授权检测。
  • 5)SessionManager(会话管理器):负责创建和管理用的session生命周期,供在所有环境中的用户强大的会话体验。
  • 6)SessionDAO:代表SessionManager执行会话持久性(CRUD)操作。 这允许将任何数据存储插入session管理基础结构。
  • 7)CacheManager(缓存管理器):创建和管理其他Shiro组件使用的Cache实例生命周期。
  • 8)Cryptographic(加密管理器):提供了加密方式的设计及管理。
  • 9)Realms(领域对象):充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器”。

一、认证业务实现

shiro入门_第3张图片

流程分析:

  • 1)调用Subject的login方法将用户信息提交给SecurityManager
  • 2.)SecurityManager将认证操作委托给认证器对象Authenticator
  • 3)Authenticator将用户输入的身份信息传递给Realm
  • 4)Realm访问数据库获取用户信息然后对信息进行封装并返回
  • 5)Authenticator 对realm返回的信息进行身份认证

1、添加shiro依赖

<dependency>
    <groupId>org.apache.shirogroupId>
    <artifactId>shiro-springartifactId>
    <version>1.5.2version>
dependency>

2、shiro核心对象配置

2.1 创建配置类,添加@Configuration注解
  • 表示为Spring配置类
2.2 创建SecurityManager对象,并交给Spring管理
  • 需要把Realms、CacheManager、SessionManager等管理器对象放入SecurityManager对象中。
2.3 创建ShiroFilterFactoryBean对象
  • 通过该对象创建过滤器工厂,再通过工厂创建过滤器,通过过滤器对请求进行过滤拦截。
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringShiroConfig {

	/**配置shiro中的核心对象:安全管理器*/
	@Bean 
	public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager, SessionManager sessionManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		return securityManager;
	}

	/**配置ShiroFilterFactoryBean对象*/
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
		ShiroFilterFactoryBean sfBean = new ShiroFilterFactoryBean();
		// 设置安全管理器
		sfBean.setSecurityManager(securityManager);
		// 设置登录页面url
		sfBean.setLoginUrl("/doLoginUI");
		// 设置过滤规则,定义map指定请求过滤规则
		LinkedHashMap<String,String> map=
				new LinkedHashMap<>();
		//静态资源允许匿名访问:"anon"
		map.put("/bower_components/**","anon");
		map.put("/build/**","anon");
		map.put("/dist/**","anon");
		map.put("/plugins/**","anon");
		map.put("/user/doLogin","anon");
		map.put("/doLogout","logout");// logout由shiro提供
		//除了匿名访问的资源,其它都要认证("authc")后访问
		map.put("/**","authc");
		sfBean.setFilterChainDefinitionMap(map);
		return sfBean;
	}
}

3、Controller业务实现

  • 调用Subject的login方法将用户信息传递给securityManager
@RequestMapping("doLogin")
	public JsonResult doLogin(String username, String password) {
		// 获取subject对象
		Subject subject = SecurityUtils.getSubject();
		// 把用户信息封装进token中
		UsernamePasswordToken token = new UsernamePasswordToken();
		token.setUsername(username);
		token.setPassword(password.toCharArray());
	
		subject.login(token);
		return new JsonResult("login OK");
	}

4、认证业务实现

业务的实现要在Realm模型对象中进行实现,要继承AuthorizingRealm并重写相关方法。

  • 重写设置凭证适配器(setCredentialsMatcher)方法
  • 重写获取认证信息方法(doGetAuthenticationInfo)方法
@Service
public class ShiroUserRealm extends AuthorizingRealm {
        @Autowired
        private SysUserDao sysUserDao;
   

        /**
         * 设置凭证匹配器(与用户添加操作使用相同的加密算法)
         */
        @Override
        public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
            //构建凭证匹配对象
            HashedCredentialsMatcher cMatcher=
            new HashedCredentialsMatcher();
            //设置加密算法
            cMatcher.setHashAlgorithmName("MD5");   
            //设置加密次数                     
            cMatcher.setHashIterations(1);
            super.setCredentialsMatcher(cMatcher);
        }

        /**
         * 通过此方法完成认证数据的获取及封装,系统
         * 底层会将认证数据传递认证管理器,由认证
         * 管理器完成认证操作。
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

            //1.获取用户名(用户页面输入)
            UsernamePasswordToken upToken = (UsernamePasswordToken)token;
            String username=upToken.getUsername();
                
            //2.基于用户名查询用户信息
            SysUser user = sysUserDao.findUserByUserName(username);

            //3.判定用户是否存在
            if (user==null)
               throw new UnknownAccountException();    

            //4.封装用户信息
            ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                               							 user,//principal (身份)
                              						     user.getPassword(),//hashedCredentials
                              						     credentialsSalt, //credentialsSalt
                                                         getName());//realName

            //5.返回封装结果
            return info;//返回值会传递给认证管理器(后续认证管理器会通过此信息完成认证操作)
        }
}

5、将Realm注入给配置类中的SecurityManager对象

@Bean
public SecurityManager securityManager(Realm realm) {
    DefaultWebSecurityManager sManager = new DefaultWebSecurityManager();
    sManager.setRealm(realm);
    return sManager;
}

二、授权业务实现

shiro入门_第4张图片

流程分析

  • 1)调用Subject的相关方法 (例如 checkPermission) 将用户信息递交给SecurityManager
  • 2)SecurityManager将权限检测操作委托给Authorizer对象
  • 3)Authorizer将用户信息委托给Realm
  • 4)Realm访问数据库获取用户权限信息并封装
  • 5)Authorizer对用户授权信息进行判定

1、在配置类中配置Bean对象的声明周期管理

  • Spring Boot可以不配置
@Bean
public LifecycleBeanPostProcessor   lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
}

2、在配置类中配置为目标业务对象创建代理对象

  • Spring Boot可以不配置
@DependsOn("lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    return new DefaultAdvisorAutoProxyCreator();
}

3、在配置类中配置advisor对象

  • shiro框架会通过此对象的matchs方法的返回值(类似于切面) 决定是否创建代理对象,进行权限控制
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
	AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
	advisor.setSecurityManager(securityManager);
    return advisor;
}

4、业务实现

  • 重写获取授权信息doGetAuthorizationInfo方法
@Service
public class ShiroUserRealm extends AuthorizingRealm {

	@Autowired
	private SysUserDao sysUserDao;

	@Autowired
	private SysUserRoleDao sysUserRoleDao;

	@Autowired
	private SysRoleMenuDao sysRoleMenuDao;

	@Autowired
	private SysMenusDao sysMenuDao;
	
	/**负责获取登录用户权限信息并进行封装*/
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//1.获取登陆用户身份信息
		SysUser user=(SysUser)principals.getPrimaryPrincipal();
		//2.基于用户id获取角色id
		List<Integer> roleIds=
				sysUserRoleDao.findRoleIdsByUserId(user.getId());
		if(roleIds==null||roleIds.size()==0)
			throw new AuthorizationException();
		//3.基于角色id获取对应的菜单id
		List<Integer> menuIds=
				sysRoleMenuDao.findMenuIdsByRoleIds(roleIds.toArray(new Integer[] {}));
		if(menuIds==null||menuIds.size()==0)
			throw new AuthorizationException();
		//4.基于菜单id获取授权标识
		List<String> permissions=
				sysMenuDao.findPermissions(menuIds.toArray(new Integer[] {}));
		if(permissions==null||permissions.size()==0)
			throw new AuthorizationException();
		//5.对用户权限信息进行封装
		Set<String> stringPermissions=new HashSet<>(); 
		for(String per:permissions) {
			if(!StringUtils.isEmpty(per)) {
				stringPermissions.add(per);
			}
		}
		SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
		info.setStringPermissions(stringPermissions);
		return info;//此对象会返回给授权管理器
	}
}

5、授权访问实现

在需要授权访问的业务成方法上,添加@RequiresPermissions(“需要的权限”)注解

/**
	 * 禁用和启用操作
	 * shiro框架通过这个注解定义切入点,在这里表示
	 * 访问此方法需要进行授权,需要具备这个注解中的权限标识
	 * 1、需要系统基于登录用户获取用户权限{"sys:user:update", "sys:user:view",...}
	 * 2、当用户包含注解中定义的权限表示,就标识用户拥有访问这个方法的权限
	 * 3=拥有权限则可以由shiro框架进行授权访问
	 */
	@RequiresPermissions("sys:user:update")
	@RequiredLog(operation = "禁用启用")
	@Override
	public int validById(Long id, Integer valid) {
		if (id == null || id < 1) 
			throw new IllegalArgumentException("id值无效");
		System.out.println(valid);
		if (valid != 0 && valid != 1) 
			throw new IllegalArgumentException("状态值不正确");
		
		int row = sysUserDao.validById(id, valid, "admin");// admin是将来的登录用户
		if (row == 0)
			throw new ServiceException("记录可能不存在!");
		return row;
	}

三、shiro扩展功能

shiro入门_第5张图片

shiro缓存配置

1、在配置类中配置缓存bean对象

@Bean
public CacheManager shiroCacheManager(){
	return new MemoryConstrainedCacheManager();
}
  • 注意该缓存对象的名字不能为cacheManager,因为spring容器中已经有一个名为cacheManager的对象了

2、将缓存对象注入给SecurityManager对象

@Bean
public SecurityManager securityManager( Realm realm, CacheManager cacheManager) {
    DefaultWebSecurityManager sManager = new DefaultWebSecurityManager();
    sManager.setRealm(realm);
    sManager.setCacheManager(cacheManager);
    return sManager;
}

Shiro RememberMe

shiro入门_第6张图片

1、在客户端提交数据时把是否 记住我 选项一并提交

isRememberMe:$("#rememberId").prop("checked")

2、在controller的登录方法进行判断

@RequestMapping("doLogin")
	public JsonResult doLogin(String username, String password, Boolean isRememberMe) {
		// 获取subject对象
		Subject subject = SecurityUtils.getSubject();
		// 把用户信息封装进token中
		UsernamePasswordToken token = new UsernamePasswordToken();
		token.setUsername(username);
		token.setPassword(password.toCharArray());
		if (isRememberMe != null) {
			token.setRememberMe(isRememberMe);
		}
		subject.login(token);
		return new JsonResult("login OK");
	}

3、在配置类中添加RememberMe管理器

	@Bean
	public RememberMeManager rememberMeManager() {
		CookieRememberMeManager cManager = new CookieRememberMeManager();
		SimpleCookie cookie = new SimpleCookie("remeberMe");
		cookie.setMaxAge(7*24*60*60);
		cManager.setCookie(cookie);
		return cManager;
	}

4、将RememberMe管理器注入securityManager中

    @Bean
	public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager, RememberMeManager rememberManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(realm);
		securityManager.setCacheManager(shiroCacheManager);
		securityManager.setRememberMeManager(rememberManager);
		return securityManager;
	}

5、修改shiro的过滤认证级别

  • map.put("/**","authc");修改为map.put("/**","user");
@Bean
	public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
		ShiroFilterFactoryBean sfBean = new ShiroFilterFactoryBean();
		// 设置安全管理器
		sfBean.setSecurityManager(securityManager);
		// 设置登录页面url
		sfBean.setLoginUrl("/doLoginUI");
		// 设置过滤规则,定义map指定请求过滤规则
		LinkedHashMap<String,String> map=
				new LinkedHashMap<>();
		//静态资源允许匿名访问:"anon"
		map.put("/bower_components/**","anon");
		map.put("/build/**","anon");
		map.put("/dist/**","anon");
		map.put("/plugins/**","anon");
		map.put("/user/doLogin","anon");
		map.put("/doLogout","logout");// logout由shiro提供
		//除了匿名访问的资源,其它都要认证("authc")后访问
		map.put("/**","user");// 设置记住我后要访问user
		sfBean.setFilterChainDefinitionMap(map);
		return sfBean;
	}

shiro会话时长配置

使用shiro框架完成认证操作后,用户登录成功会将用户的信息写入到会话对象中,默认时长为30分钟。

1、在shiro配置类中,添加会话管理器的配置

    @Bean
	public SessionManager sessionManager() {
		DefaultWebSessionManager sManager = new DefaultWebSessionManager();
		sManager.setGlobalSessionTimeout(60*60*24);
		return sManager;
	}

2、把会话管理器注入SecurityManager

@Bean
	public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager,
										   RememberMeManager rememberManager, SessionManager sessionManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(realm);
		securityManager.setCacheManager(shiroCacheManager);
		securityManager.setRememberMeManager(rememberManager);
		securityManager.setSessionManager(sessionManager);
		return securityManager;
	}

你可能感兴趣的:(框架)