Shiro集成spring企业级开发

1.核心类ShiroFilterFactoryBean

	<!-- 安全认证过滤器 -->
	<bean id="shiroFilter" class="com.dq.shiro.security.MyShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="${adminPath}/login" />
		<property name="successUrl" value="${adminPath}?login" />
		<property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <!-- 表单校验过滤器 ref-->
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
		<property name="filterChainDefinitions">
			<ref bean="shiroFilterChainDefinitions"/>
		</property>
	</bean>
重要引用

①securityManager(shiro安全管理器)
②filterChainDefinitions(可以定义指定url需要的认证方式)

	<bean name="shiroFilterChainDefinitions" class="java.lang.String">
		<constructor-arg>
			<value>
				/static/** = anon
				${adminPath}/login = authc
				${adminPath}/logout = logout
				${adminPath}/** = user
			</value>
		</constructor-arg>
	</bean>
③filters中定义各种自定义filters

注意在这个Filter中,username password rememberMe是写死的,所以前台传递参数时需要注意比对,也可以在配置文件中修改

	<!-- 表单校验过滤器 -->
	<bean id="formAuthenticationFilter" class="com.dq.shiro.security.FormAuthenticationFilter">
		<!-- 表单中账号的input名称 -->
		<property name="usernameParam" value="username" />
		<!-- 表单中密码的input名称 -->
		<property name="passwordParam" value="password" />
	</bean>

public class FormAuthenticationFilter extends AuthenticatingFilter {

    //TODO - complete JavaDoc

    public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure";

    public static final String DEFAULT_USERNAME_PARAM = "username";
    public static final String DEFAULT_PASSWORD_PARAM = "password";
    public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";

比如重写的formAuthenticationFilter

public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {

	public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
	public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";
	public static final String DEFAULT_USERNAME_PARAM = "username";
	public static final String DEFAULT_PASSWORD_PARAM = "loginPwd";
	public static final String DEFAULT_MESSAGE_PARAM = "message";

	private String captchaParam = DEFAULT_CAPTCHA_PARAM;
	private String mobileLoginParam = DEFAULT_MOBILE_PARAM;
	private String messageParam = DEFAULT_MESSAGE_PARAM;
	private String usernameParam = "username";
	private String passwordParam = "loginPwd";

	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request);
		String password = getPassword(request);
		if (password==null){
			password = "";
		}
		boolean rememberMe = isRememberMe(request);
		String host = com.dq.base.web.utils.WebUtils.getIpAddr((HttpServletRequest)request);
		String captcha = getCaptcha(request);
		boolean mobile = isMobileLogin(request);
		return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile);
	}

	public String getCaptchaParam() {
		return captchaParam;
	}

	protected String getCaptcha(ServletRequest request) {
		return WebUtils.getCleanParam(request, getCaptchaParam());
	}

	public String getMobileLoginParam() {
		return mobileLoginParam;
	}
	
	protected boolean isMobileLogin(ServletRequest request) {
        return WebUtils.isTrue(request, getMobileLoginParam());
    }
	
	public String getMessageParam() {
		return messageParam;
	}
	
	/**
	 * 登录成功之后跳转URL
	 */
	public String getSuccessUrl() {
		return super.getSuccessUrl();
	}
	
	@Override
	protected void issueSuccessRedirect(ServletRequest request,
			ServletResponse response) throws Exception {
//		Principal p = UserUtils.getPrincipal();
//		if (p != null && !p.isMobileLogin()){
			 WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
//		}else{
//			super.issueSuccessRedirect(request, response);
//		}
	}

	/**
	 * 登录失败调用事件
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token,
			AuthenticationException e, ServletRequest request, ServletResponse response) {
		String className = e.getClass().getName(), message = "";
		if (IncorrectCredentialsException.class.getName().equals(className)
				|| UnknownAccountException.class.getName().equals(className)){
			message = "用户或密码错误, 请重试.";
		}
		else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")){
			message = StringUtils.replace(e.getMessage(), "msg:", "");
		}
		else{
			message = "系统出现点问题,请稍后再试!";
			e.printStackTrace(); // 输出到控制台
		}
        request.setAttribute(getFailureKeyAttribute(), className);
        request.setAttribute(getMessageParam(), message);
        return true;
	}
	
	protected String getPassword(ServletRequest request) {
		return WebUtils.getCleanParam(request, getPasswordParam());
	}
	
	public String getUsernameParam() {
		return this.usernameParam;
	}

	public void setUsernameParam(String usernameParam) {
		this.usernameParam = usernameParam;
	}

	public String getPasswordParam() {
		return this.passwordParam;
	}

	public void setPasswordParam(String passwordParam) {
		this.passwordParam = passwordParam;
	}
	
}
shiro进行初始化时,会调用ShiroFilterFactoryBean中的createInstance方法

	@Override
	protected AbstractShiroFilter createInstance() throws Exception {
		org.apache.shiro.mgt.SecurityManager securityManager = getSecurityManager();
		if (securityManager == null) {
			String msg = "SecurityManager property must be set.";
			throw new BeanInitializationException(msg);
		}

		if (!(securityManager instanceof WebSecurityManager)) {
			String msg = "The security manager does not implement the WebSecurityManager interface.";
			throw new BeanInitializationException(msg);
		}
		FilterChainManager manager = createFilterChainManager();

		PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
		chainResolver.setFilterChainManager(manager);

		return new MySpringShiroFilter((WebSecurityManager) securityManager,
				chainResolver);
	}
初始化SecurityManager

并且执行Filter链

2.SecurityManager

	<!-- 定义Shiro安全管理配置 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="systemAuthorizingRealm" />
		<property name="sessionManager" ref="sessionManager" />
		<property name="cacheManager" ref="shiroCacheManager" />
	</bean>
重要引用

①realm 

包含两个重要方法 认证+授权

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) 

realm中,可以修改

  1. @PostConstruct    
  2.     public void initCredentialsMatcher() {    
  3. //该句作用是重写shiro的密码验证,让shiro用我自己的验证    
  4.         setCredentialsMatcher(new CustomCM());    
  5.     
  6.     }    
    1. import org.apache.shiro.authc.AuthenticationInfo;  
    2. import org.apache.shiro.authc.AuthenticationToken;  
    3. import org.apache.shiro.authc.UsernamePasswordToken;  
    4. import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;  
    5. import org.apache.shiro.crypto.hash.Sha384Hash;  
    6.   
    7. import com.sicc.oa.common.pass.SHA1;  
    8.   
    9. /** 
    10.  * 自定义 密码验证类 
    11.  * 
    12.  * @author q 
    13.  * 
    14.  */  
    15. public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {  
    16.     @Override  
    17.     public boolean doCredentialsMatch(AuthenticationToken authcToken,  
    18.             AuthenticationInfo info) {  
    19.         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
    20.   
    21.         Object tokenCredentials = encrypt(String.valueOf(token.getPassword()));  
    22.         Object accountCredentials = getCredentials(info);  
    23.         return equals(tokenCredentials, accountCredentials);  
    24.     }  
    25.   
    26.     // 将传进来密码加密方法  
    27.     private String encrypt(String data) {  
    28.         String sha384Hex = new SHA1().getDigestOfString(String.valueOf(data)  
    29.                 .getBytes());  
    30.         ;// 这里可以选择自己的密码验证方式 比如 md5或者sha256等  
    31.         return sha384Hex;  
    32.     }  

进行密码的验证方式修改(使用sha-1算法,进行1024次散列),这个方法就是盐

	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(HASH_ALGORITHM);
		matcher.setHashIterations(HASH_INTERATIONS);
		setCredentialsMatcher(matcher);
	}

这是在ShiroFilterFacotoryBean初始化时就已经设置好的

注意如果不进行这个initCredentialsMatcher那个realm是不知道盐的算法的,需要在spring中配置

②sessionManager(会话管理器)

处理会话的各种事物,包括session的存储方式,session失效处理,过期处理等

③cacheManager(缓存管理器)

3.SessionManager 会话管理器

	<!-- 自定义会话管理配置 -->
	<bean id="sessionManager" class="com.dq.shiro.security.SessionManager"> 
		<property name="sessionDAO" ref="sessionDAO"/>
		<!-- 配置会话监听器 -->
		<property name="sessionListeners" ref="mySessionListener" />  
		<!-- 会话超时时间,单位:毫秒  -->
		<property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
		
		<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->
		<property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
<!--  		<property name="sessionValidationSchedulerEnabled" value="false"/> -->
 		<property name="sessionValidationSchedulerEnabled" value="true"/>
 		
		<property name="sessionIdCookie" ref="sessionIdCookie"/>
		<property name="sessionIdCookieEnabled" value="true"/>
	</bean>

重要子类

①sessionDAO 用于对session信息的crud操作,即可以进行session持久化,并且指定用什么方式去保存session(redis echcache memcached)

②sessionListener 可以用于监听会话

public class MySessionListener implements SessionListener{
	
	private Logger logger = LoggerFactory.getLogger(MySessionListener.class);
	
	@Override
	public void onStart(Session session) {
		logger.info("会话**********************************");	
		logger.info("会话**********************************");	
		logger.info("会话创建:sessionId为" + session.getId());		
		logger.info("会话**********************************");	
		logger.info("会话**********************************");	
	}

	@Override
	public void onStop(Session session) {
		logger.info("会话**********************************");	
		logger.info("会话**********************************");
		logger.info("会话退出:sessionId为" + session.getId());		
		logger.info("会话**********************************");	
		logger.info("会话**********************************");
	}

	@Override
	public void onExpiration(Session session) {
		logger.info("会话**********************************");	
		logger.info("会话**********************************");
		logger.info("会话过期:sessionId为" + session.getId());		
		logger.info("会话**********************************");	
		logger.info("会话**********************************");
	}

}
4.SessionDAO

	<!-- 自定义Session存储容器 -->
	<bean id="sessionDAO" class="com.dq.shiro.security.CacheSessionDAO">
		<property name="sessionIdGenerator" ref="sessionIdGen" />
		<property name="activeSessionsCacheName" value="activeSessionsCache" />
		<property name="cacheManager" ref="shiroCacheManager" />
	</bean>
主要子类

①sessionIdGenerator
生成sessionId 可以自定义

public class SessionIdGen implements SessionIdGenerator {

	/**
	 * @Description: TODO(Session ID 生成)
	 * @param: @param session
	 * @param: @return
	 * @throws
	 */
	@Override
	public Serializable generateId(Session session) {
		return IdGen.uuid();
	}
}
②cacheManager(缓存管理器)

5.cacheManager缓存管理器

	<!-- 定义授权缓存管理器 -->
	<!-- 	<bean id="shiroCacheManager" class="com.minstone.common.security.shiro.cache.SessionCacheManager" // -->
	<bean id="shiroCacheManager" class="com.dq.shiro.cache.RedisCacheManager">
	    <property name="redisManager" ref="redisManager" />
	</bean>
public class RedisCacheManager implements CacheManager {

	private static final Logger logger = LoggerFactory
			.getLogger(RedisCacheManager.class);

	// fast lookup by name map
	@SuppressWarnings("rawtypes")
	private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();

	private RedisManager redisManager;

	/**
	 * The Redis key prefix for caches 
	 */
	private String keyPrefix = "shiro_redis_cache:";
	
	/**
	 * Returns the Redis session keys
	 * prefix.
	 * @return The prefix
	 */
	public String getKeyPrefix() {
		return keyPrefix;
	}

	/**
	 * Sets the Redis sessions key 
	 * prefix.
	 * @param keyPrefix The prefix
	 */
	public void setKeyPrefix(String keyPrefix) {
		this.keyPrefix = keyPrefix;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
		logger.debug("获取名称为: " + name + " 的RedisCache实例");
		
		Cache c = caches.get(name);
		
		if (c == null) {

			// initialize the Redis manager instance
			redisManager.init();
			
			// create a new cache instance
			c = new RedisCache<K, V>(redisManager, keyPrefix);
			
			// add it to the cache collection
			caches.put(name, c);
		}
		return c;
	}

	public RedisManager getRedisManager() {
		return redisManager;
	}

	public void setRedisManager(RedisManager redisManager) {
		this.redisManager = redisManager;
	}
	
}
自定义的缓存处理器需要实现org.apache.shiro.cache.CacheManager

实现的方法

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
		logger.debug("获取名称为: " + name + " 的RedisCache实例");
		
		Cache c = caches.get(name);
		
		if (c == null) {

			// initialize the Redis manager instance
			redisManager.init();
			
			// create a new cache instance
			c = new RedisCache<K, V>(redisManager, keyPrefix);
			
			// add it to the cache collection
			caches.put(name, c);
		}
		return c;
	}

6.redisManager

一个直接通过jedisPool操作jedis的类

无须赘述

7.运行流程

登录项目

首先会被Shiro核心Filter(ShiroFilterFacotryBean)中的Filter进行拦截

tips:过滤链的注释和例子

过滤器简称	对应的java类
anon	org.apache.shiro.web.filter.authc.AnonymousFilter
authc	org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic	org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms	org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port	org.apache.shiro.web.filter.authz.PortFilter
rest	org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles	org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl	org.apache.shiro.web.filter.authz.SslFilter
user	org.apache.shiro.web.filter.authc.UserFilter
logout	org.apache.shiro.web.filter.authc.LogoutFilter


anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数 
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值