shiro 使用

  •  shiro 核心架构

  1. Subject(主体):与软件交互的一个特定的实体(用户、第三方服务等)。
  2. SecurityManager(安全管理器) :Shiro 的核心,用来协调管理组件工作。
  3. Authenticator(认证管理器):负责执行认证操作
  4. Authorizer(授权管理器):负责授权检测
  5. SessionManager(会话管理):负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。
  6. SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。
  7. CacheManager(缓存管理器):提供创建缓存实例和管理缓存生命周期的功能
  8. Cryptography(加密管理器):提供了加密方式的设计及管理。
  9. Realms(领域对象):是shiro和你的应用程序安全数据之间的桥梁。

 

  • shiro 认证

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

 

  • shiro 授权

  1. 系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager
  2. SecurityManager将权限检测操作委托给Authorizer对象,Authorizer将用户信息委托给realm.
  3. Realm访问数据库获取用户权限信息并封装。
  4. Authorizer对用户授权信息进行判定。

 


  • 添加依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

 

  • web.xml
	<!-- 
		1)	targetBeanName 名字由DelegatingFilterProxy对象底层设置并读取。
		2)	shiroFilterFactory 名字要与ShiroFilterFactoryBean配置的id相同。
	 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilterFactory</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
  • spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
       
	<!-- shiro过滤与认证 -->
	<bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="SecurityManager" ref="securityManager" />
		<property name="LoginUrl" value="/doLoginUI.do" />
		<!-- 设置请求过滤规则 -->
		<property name="FilterChainDefinitionMap">
			<map>
				<!--anon 匿名可访问,authc 必须认证,假如添加了记住我功能以后需要设置为user -->
				<entry key="/bower_components/**" value="anon" />
				<entry key="/build/**" value="anon" />
				<entry key="/dist/**" value="anon" />
				<entry key="/plugins/**" value="anon" />
				<entry key="/user/doLogin.do" value="anon" />
				<entry key="/doLogout.do" value="logout" />
				<!--<entry key="/**" value="authc" /> 必须认证 -->
				<entry key="/**" value="user" />
			</map>
		</property>
	</bean>
	
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 认证与授权,在 ShiroUserRealm 类中重写各自方法-->
		<property name="Realm" ref="shiroUserRealm" />
		<!-- shiro缓存 -->
		<property name="CacheManager" ref="cacheManager" />
		<!-- remember me -->
		<property name="RememberMeManager" ref="rememberMeManager" />
		<!-- session -->
		<property name="sessionManager" ref="sessionManager"/>
	</bean>

	<!-- shiro授权 -->
	<!-- 授权属性的Advisor配置 -->
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="SecurityManager" ref="securityManager" />
	</bean>
	<!-- 配置bean对象的生命周期管理 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
	<!-- 通过此配置要为目标业务对象创建代理对象 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
		depends-on="lifecycleBeanPostProcessor">
	</bean>


	<!-- shiro缓存 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

	<!-- remember me -->
	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<constructor-arg value="rememberMe" />
		<!-- 7天,采用spring el表达式来计算,方便修改 -->
		<property name="maxAge" value="#{7 * 24 * 60 * 60}" />
	</bean>
	<bean id="rememberMeManager"
		class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cookie" ref="rememberMeCookie" />
	</bean>

	<!-- shiro结合Session会话管理器 start -->
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<!-- session的失效时长,单位毫秒 1小时: 3600000, itzixi站点设置以 6小时 为主:21600000 -->
		<!-- 设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期 1800000 -->
		<property name="globalSessionTimeout" value="21600000" />
		<!-- 删除失效的session -->
		<property name="deleteInvalidSessions" value="true" />
		<!-- 是否开启会话验证器,默认是开启的 -->
		<property name="sessionValidationSchedulerEnabled" value="true" />
	</bean>
	
</beans>
  • service 层新建类 ShiroUserRealm 继承AuthorizingRealm  接口,重写3个方法
@Service
public class ShiroUserRealm extends AuthorizingRealm {

	@Autowired
	private SysUserDao sysUserDao;

	@Autowired
	private SysUserRoleDao sysUserRoleDao;

	@Autowired
	private SysRoleMenuDao sysRoleMenuDao;

	@Autowired
	private SysMenuDao sysMenuDao;

	/** 通过此方法完成授权信息的获取及封装,在service 层的方法使用@RequiresPermissions("sys:user:valid")来允许拥有sys:user:valid 权限的用户调用方法 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// 1.获取登录用户信息,例如用户id
		SysUser user = (SysUser) principals.getPrimaryPrincipal();
		Integer userId = user.getId();
		
		// 2.基于用户id获取用户拥有的角色(sys_user_roles)
		List<Integer> roleIds = sysUserRoleDao.findRoleIdsByUserId(userId);
		if (roleIds == null || roleIds.size() == 0)
			throw new AuthorizationException();
		
		// 3.基于角色id获取菜单id(sys_role_menus)
		Integer[] array = {};
		List<Integer> menuIds = sysRoleMenuDao.findMenuIdsByRoleIds(roleIds.toArray(array));
		if (menuIds == null || menuIds.size() == 0)
			throw new AuthorizationException();
		
		// 4.基于菜单id获取权限标识(sys_menus)
		List<String> permissions = sysMenuDao.findPermissions(menuIds.toArray(array));
		
		// 5.对权限标识信息进行封装并返回
		Set<String> set = new HashSet<>();
		for (String per : permissions) {
			if (!StringUtils.isEmpty(per)) {
				set.add(per);
			}
		}
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setStringPermissions(set);
		return info;// 返回给授权管理器
	}

	/**
	 * 设置凭证匹配器(与用户添加操作使用相同的加密算法)
	 */
	@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.判定用户是否已被禁用。
		if (user.getValid() == 0)
			throw new LockedAccountException();

		// 5.封装用户信息
		ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
		// 记住:构建什么对象要看方法的返回值
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, // principal (身份)
				user.getPassword(), // hashedCredentials,token 存有前端传来的密码,这里是数据库取的密码
				credentialsSalt, // credentialsSalt,每个账号的盐值都不同,所以要从数据库获取账户的盐值
				getName());// realName
		// 6.返回封装结果
		return info;// 返回值会传递给认证管理器(后续
		// 认证管理器会通过此信息完成认证操作)

	}

}
  • 业务Service 层

保存用户,保存密码代码片段

        // 将数据写入数据库,保存密码部分
		String salt = UUID.randomUUID().toString();
		entity.setSalt(salt);
		// 加密
		SimpleHash sHash = new SimpleHash("MD5", entity.getPassword(), salt, 1);
		entity.setPassword(sHash.toHex());

 更新密码

@Override
	public int updatePassword(String password, String newPassword, String cfgPassword) {
		// 1.判定新密码与密码确认是否相同
		if (StringUtils.isEmpty(newPassword))
			throw new IllegalArgumentException("新密码不能为空");
		if (StringUtils.isEmpty(cfgPassword))
			throw new IllegalArgumentException("确认密码不能为空");
		if (!newPassword.equals(cfgPassword))
			throw new IllegalArgumentException("两次输入的密码不相等");
		// 2.判定原密码是否正确
		if (StringUtils.isEmpty(password))
			throw new IllegalArgumentException("原密码不能为空");
		// 获取登陆用户
		SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();//获取的是当前登陆用户
		SimpleHash sh = new SimpleHash("MD5", password, user.getSalt(), 1);
		if (!user.getPassword().equals(sh.toHex()))
			throw new IllegalArgumentException("原密码不正确");
		// 3.对新密码进行加密
		String salt = UUID.randomUUID().toString();
		sh = new SimpleHash("MD5", newPassword, salt, 1);
		// 4.将新密码加密以后的结果更新到数据库
		int rows = sysUserDao.updatePassword(sh.toHex(), salt, user.getId());
		if (rows == 0)
			throw new ServiceException("修改失败");
		return rows;
	}
  • Controller 层
	@RequestMapping("/doLogin")
	@ResponseBody
	public ReturnJson doLogin(boolean isRememberMe, String username, String password) {
		// 1.获取Subject对象
		Subject subject = SecurityUtils.getSubject();
		// 2.通过Subject提交用户信息,交给shiro框架进行认证操作
		// 2.1对用户进行封装
		UsernamePasswordToken token = new UsernamePasswordToken(
				username, // 身份信息
				password);// 凭证信息
		if (isRememberMe) {
			token.setRememberMe(true);
		}
		// 2.2对用户信息进行身份认证
		subject.login(token);
		// 分析:
		// 1)token会传给shiro的SecurityManager
		// 2)SecurityManager将token传递给认证管理器
		// 3)认证管理器会将token传递给realm
		return new ReturnJson("login ok");
	}

  • 另java 类配置shiro
package com.db.common.config;

import java.util.LinkedHashMap;
import java.util.Map;

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.spring.LifecycleBeanPostProcessor;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.db.sys.service.realm.ShiroUserRealm;

/** @Configuration 描述的bean一般为一个配置类 */
@Configuration
public class SpringShiroConfig {
	/**
	 * @Bean一般用户描述方法,然后将方法的返回值 交给Spring管理,其中@Bean注解中的内容为Bean 对象的key。
	 * @return
	 */
	@Bean("securityManager")
	public SecurityManager newSecurityManager(ShiroUserRealm realm, CacheManager cacheManager,
			RememberMeManager rememberMeManager) {
		DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
		sm.setRealm(realm);
		sm.setCacheManager(cacheManager);
		sm.setRememberMeManager(rememberMeManager);
		sm.setSessionManager(newSessionManager());
		return sm;// 不是java.lang包中的SecurityManager
	}

	@Bean("shiroFilterFactory")
	public ShiroFilterFactoryBean newShiroFilterFactoryBean(SecurityManager securityManager) {
		// 1.构建ShiroFilterFactoryBean对象(负责创建ShiroFilter工厂对象)
		ShiroFilterFactoryBean fBean = new ShiroFilterFactoryBean();
		// 2.设置安全管理器
		fBean.setSecurityManager(securityManager);
		// 3.设置登录页面对应的url(非认证用户要跳转到此url对应的页面)
		fBean.setLoginUrl("/doLoginUI.do");
		// 4.设置过滤规则(哪些允许匿名访问,哪些需要认证访问)
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
		filterMap.put("/bower_components/**", "anon");
		filterMap.put("/build/**", "anon");
		filterMap.put("/dist/**", "anon");
		filterMap.put("/plugins/**", "anon");
		filterMap.put("/user/doLogin.do", "anon");
		filterMap.put("/doLogout.do", "logout");
		filterMap.put("/**", "user");
		fBean.setFilterChainDefinitionMap(filterMap);
		return fBean;
	}

	// ==========================
	// @Bean注解没有指定名字时,默认bean的名字为方法名
	@Bean("lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor newLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@DependsOn("lifecycleBeanPostProcessor")
	@Bean
	public DefaultAdvisorAutoProxyCreator DefaultAdvisorAutoProxyCreator() {
		return new DefaultAdvisorAutoProxyCreator();
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor newAuthorizationAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}

	// 配置缓存管理器(可以缓存用户的权限信息)
	@Bean
	public MemoryConstrainedCacheManager newCacheManager() {
		return new MemoryConstrainedCacheManager();
	}

	// 配置记住我
	@Bean
	public CookieRememberMeManager newCookieManager() {
		CookieRememberMeManager cookieManager = new CookieRememberMeManager();
		SimpleCookie cookie = new SimpleCookie("rememberMe");
		cookie.setMaxAge(24 * 7 * 60 * 60);
		cookieManager.setCookie(cookie);
		return cookieManager;
	}

	// 配置session管理器
	public DefaultWebSessionManager newSessionManager() {
		DefaultWebSessionManager sManager = new DefaultWebSessionManager();
		sManager.setGlobalSessionTimeout(21600000);
		sManager.setDeleteInvalidSessions(true);
		sManager.setSessionValidationSchedulerEnabled(true);
		return sManager;
	}
}
package com.db.common.config;
import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

//替换web.xml (参考官网实现)
public class AppWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
	//其中前端控制器的配置是在父类的onStart方法实现的。
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerFilter(servletContext);
		super.onStartup(servletContext);
	}
	private void registerFilter(ServletContext servletContext) {
		Dynamic d = servletContext.addFilter("shiroFilter",
		DelegatingFilterProxy.class);
		d.setInitParameter("targetBeanName","shiroFilterFactory");
		d.addMappingForUrlPatterns(null,true,"/*");
	}
	/**
          * 配置@Service,@Repository
     */
	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {SpringRespositoryConfig.class,SpringServiceConfig.class};
	}
	/**加载spring mvc配置*/
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] {SpringWebConfig.class} ;
	}
	/**定义映射路径(spring mvc要处理哪些url)*/
	@Override
	protected String[] getServletMappings() {
		System.out.println("getServletMappings");
		return new String[] {"/"};
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值