shiro的配置详解

1.下载shiro

http://shiro.apache.org/download.html

2.shiro架构的核心内容介绍

Subject应用代码直接交互的对象是 Subject, Subject 的所有交互都会委托给 SecurityManager;  

②SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且其管理着所有 Subject

③RealmShiro 从 Realm 获取安全数据(如用户、角色、权限),就是说  SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作.

3.引入shiro依赖

<properties>
		<shiro.version>1.2.3</shiro.version>
	</properties>
<!-- 引入ehcache的依赖 -->
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache-core</artifactId>
			<version>2.6.6</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<!--Apache Shiro所需的jar包 start -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<!--Apache Shiro所需的jar包 end -->

4.首先在web.xml中加入shiro过滤器

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
</filter-mapping>

5.添加shiro与spring集成的配置文件application-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- shiro的核心 所有安全操作都将通过securityManager来处理 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 缓存管理器 -->
		<property name="cacheManager" ref="cacheManager" />
		<!-- session模式 native本地 http网络 -->
		<!-- <property name="sessionMode" value="native" /> -->
		<!-- realm 获取安全数据(如用户、角色、权限) -->
		<property name="realm" ref="jdbcRealm" />
	</bean>
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO"/>
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<property name="sessionDAO" ref="sessionDAO"></property>
	</bean>
	<!-- 缓存管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
	</bean>

	<!-- 查询安全数据(用户,角色,权限等) -->
	<bean id="jdbcRealm" class="com.plat.shiro.CustomJdbcRealm">
		<!-- 数据源 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 开启查询权限 -->
		<property name="permissionsLookupEnabled" value="true"></property>
		<!-- 授权登录sql -->
		<property name="authenticationQuery"
			value="SELECT su.password FROM sys_user su WHERE su.account = ? and su.is_use =1"></property>
		<!-- 查询角色sql -->
		<property name="userRolesQuery"
			value="SELECT sr.rkey FROM sys_role sr,sys_user su,sys_user_role sur  WHERE su.user_code = sur.user_code and sur.role_code = sr.rcode  and su.account=?"></property>
		<!-- 查询权限sql -->
		<property name="permissionsQuery"
			value="SELECT sp.pkey FROM sys_permission sp ,sys_role sr,sys_role_permission srp WHERE sr.rcode = srp.role_code and sp.pcode = srp.permission_code and sr.rkey=?"></property>
	</bean>

	<!-- 将shiro bean的生命周期交给spring管理 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!-- 开启Shiro的注解 -->
	<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>

    <!-- 与web.xml中shiro过滤器的名字必须相同-->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/login" />
		<property name="successUrl" value="/index" />
		<!-- 未授权跳转地址 -->
<!-- 		<property name="unauthorizedUrl" value="/s/unauthorized" /> -->
		<!-- 
			?   匹配任意一个字符   /login?   /login1  /logina
			*   匹配任意字符          /login*   /login123 /loginabc123 
			/** 匹配任意地址          /login/** /login/xxx/xxx/xxx 
			
			shiro 过滤器
				anon  无需授权即可访问
				authc 必须登录才能访问,不包括记住我登录
				user  授权即可访问,包括记住我登录
				logout 退出拦截器 
		 -->
		<property name="filterChainDefinitions">
			<value>
				/**/*login*/**=anon
				/resources/**=anon
				/**/*logout*/**=logout
				/**=user
			</value>
		</property>
	</bean>

</beans>

6.缓存管理器ehcache.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 	<diskStore path="G:\\shopCache" /> -->
	<!-- 如果缓存内存溢出,则存储到储存空间 -->
	<diskStore path="java.io.tmpdir" />
	<defaultCache maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />
	    <!--配置自定义缓存                maxElementsInMemory:缓存中允许创建的最大对象数           
		    eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。                timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,                       
		                    两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,                       
		                    如果该值是 0 就意味着元素可以停顿无穷长的时间。                timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,                                      这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。           
		    overflowToDisk:内存不足时,是否启用磁盘缓存。                memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。 
		FIFO 先进先出策略,此算法已被淘汰 LRU 距离访问最远的先被踢(时间策略),会忽略访问频率 LFU 最未使用算法(频率优先) 会忽略访问先后时间       
		     -->
	<cache name="shopCache" maxElementsInMemory="10000" eternal="true"
		overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="600"
		memoryStoreEvictionPolicy="LFU" />
</ehcache>

7.自定义realm

package com.plat.shiro;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.session.Session;

import com.plat.commons.model.SysUser;
import com.plat.service.SysUserService;

public class CustomJdbcRealm extends JdbcRealm {

	@Resource
	private SysUserService sysUserService;
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("------doGetAuthenticationInfo-------");
		/**
		 * js --> sysUser/login --> Subject.login(UsernamepasswordToken) --> CustomJdbcRealm
		 */
		
		UsernamePasswordToken usernamePasswordToken =(UsernamePasswordToken) token;
		//获取登录账号
		String account=usernamePasswordToken.getUsername();
		SysUser sysUser=sysUserService.findByAccount(account);
		if(sysUser==null) {
			throw new UnknownAccountException();
		}
		//判断用户名和密码是否正确
		if(!sysUser.getPassword().equals(new String (usernamePasswordToken.getPassword()))) {
			throw new IncorrectCredentialsException();
		}
		//判断用户是否被禁用
		if(sysUser.getIsUse()==0) {
			throw new LockedAccountException();
		}
		
		//用户登录成功,将用户保存到session中
		Session session= SecurityUtils.getSubject().getSession();
		session.setAttribute("sysUser", sysUser);
		
		//参数1:Subject.getPrincipal()获取到的就是参数一传入的内容
		//参数2:shiro进行用户信息比对的时候使用的密码
		//参数3:realm名称,getName方法自动生成一个realm的名称
		return new SimpleAuthenticationInfo(account,sysUser.getPassword(),getName());
	}
}

8.登录方法

@RequestMapping("/login")
	@ResponseBody
	public String login(String account,String password) {
		//返回信息
		String message = "";
		if(!isNotNull(account)) {
			message="登录账号不能为空";
		}
		
		if(message=="" && !isNotNull(password)) {
			message="登录密码不能为空";
		}
		//登录
		if(!isNotNull(message)) {
			Subject currentUser=SecurityUtils.getSubject();
			
			//加密密码
			String md5Password = DigestUtil.hmacSign(password, DigestUtil.digest("heheda"));
			
			UsernamePasswordToken upToken=
					new UsernamePasswordToken(account,md5Password);
			//记住我
			//upToken.setRememberMe(true);
			try {
                //进入shiro的登录,此时将进入自定义的realm中进行安全验证
				currentUser.login(upToken);
				message="success";
			}catch (UnknownAccountException uae) {
				message="账号不存在";
			}catch (IncorrectCredentialsException ice) {
				message="用户名或者密码错误";
			}catch (LockedAccountException lae) {
				message="账号已被锁定";
			} catch (AuthenticationException e) {
				message="登录异常,请稍后再试!!";
				e.printStackTrace();
			}
		}
		return message;
	}

9.MD5

package com.plat.commons.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class DigestUtil {

	private static String encodingCharset = "UTF-8";

	/**
	 * @param aValue
	 * @param aKey
	 * @return
	 */
	public static String hmacSign(String aValue, String aKey) {
		byte k_ipad[] = new byte[64];
		byte k_opad[] = new byte[64];
		byte keyb[];
		byte value[];
		try {
			keyb = aKey.getBytes(encodingCharset);
			value = aValue.getBytes(encodingCharset);
		} catch (UnsupportedEncodingException e) {
			keyb = aKey.getBytes();
			value = aValue.getBytes();
		}

		Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
		Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
		for (int i = 0; i < keyb.length; i++) {
			k_ipad[i] = (byte) (keyb[i] ^ 0x36);
			k_opad[i] = (byte) (keyb[i] ^ 0x5c);
		}

		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {

			return null;
		}
		md.update(k_ipad);
		md.update(value);
		byte dg[] = md.digest();
		md.reset();
		md.update(k_opad);
		md.update(dg, 0, 16);
		dg = md.digest();
		return toHex(dg);
	}

	public static String toHex(byte input[]) {
		if (input == null)
			return null;
		StringBuffer output = new StringBuffer(input.length * 2);
		for (int i = 0; i < input.length; i++) {
			int current = input[i] & 0xff;
			if (current < 16)
				output.append("0");
			output.append(Integer.toString(current, 16));
		}

		return output.toString();
	}

	/**
	 * 
	 * @param args
	 * @param key
	 * @return
	 */
	public static String getHmac(String[] args, String key) {
		if (args == null || args.length == 0) {
			return (null);
		}
		StringBuffer str = new StringBuffer();
		for (int i = 0; i < args.length; i++) {
			str.append(args[i]);
		}
		return (hmacSign(str.toString(), key));
	}

	/**
	 * @param aValue
	 * @return
	 */
	public static String digest(String aValue) {
		aValue = aValue.trim();
		byte value[];
		try {
			value = aValue.getBytes(encodingCharset);
		} catch (UnsupportedEncodingException e) {
			value = aValue.getBytes();
		}
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("SHA");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
		return toHex(md.digest(value));

	}

	public static void main(String[] args) {
        // 参数1: 明文(要加密的数据) 参数2: 密钥
		System.out.println(DigestUtil.hmacSign("11111", "dddd"));
	}
}

猜你喜欢

转载自blog.csdn.net/zzwforbid_404/article/details/82015253