spring整合shiro

发表时间:2018-03-20 09:33:52 浏览量( 7 ) 留言数( 0 )

学习目标:

1、了解Spring整合Shiro的步骤

2、初步了解Shiro的拦截器


学习过程:

   http://jinnianshilongnian.iteye.com/blog/2024723   

一、搭建环境

1、导入相关的包,搭建好spring和shiro的包,pom.xml配置如下:

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>4.3.2.RELEASE</spring.version>
		<slf4j.version>1.7.12</slf4j.version>
		<logback.version>1.1.2</logback.version>
		<shiro.version>1.3.2</shiro.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.0.1</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-all</artifactId>
			<version>${shiro.version}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-cas</artifactId>
			<version>${shiro.version}</version>
		</dependency>
	</dependencies>

2、web.xml添加shiro Filter

    Shiro提供了与Web集成的支持,通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,是安全控制的入口点,也可以负责读取配置(如ini配置文件).

	<!-- shiro的filter -->
	<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 设置true由servlet容器控制filter的生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean -->
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

3、整合Spring的配置shiroContext.xml

   配置信息其实和前面的写法差不多的。这里的配置我们还是用了MD5的安全配置。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd     http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">


	<!-- 开启aop,对类代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	<!-- 开启shiro注解支持 -->
	<bean
		class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<property name="unauthorizedUrl" value="/refuse.do" />
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">
			<value>
				<!-- 退出拦截,由shiro拦截并实现,所以你不需要自己在mvc配置这样的一个方法了。 -->
				/logout.do = logout
				<!-- 不需要权限拦截 -->
				/do.jsp = anon

				/js/** anon
				/images/** anon
				/styles/** anon
				/validatecode.jsp anon

				<!-- roles[XX]表示有XX角色才可访问 -->
				/item/list.action = roles[item],authc

				<!-- user表示身份认证通过或通过记住我认证通过的可以访问 -->
				/** = authc
			</value>
		</property>
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myCustomRealm" />
	</bean>

	<!-- 凭证匹配器 -->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="1" />
	</bean>

	<!-- 自定义 realm -->
	<bean id="myCustomRealm" class="com.shiro.MyCustomRealm">
		<property name="credentialsMatcher" ref="credentialsMatcher" />
	</bean>
	 
   <bean id="exceptionResolver" class="com.shiro.MyExceptionResolver"></bean>  

</beans>


4、自定义realm

   此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。为了和数据库信息更加对应,这里我还定义了一个User对象,记录用户的登陆信息。

public class MyCustomRealm extends AuthorizingRealm {

	@Override
	public String getName() {
		return "myCustomRealm";
	}

	// 支持什么类型的token
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof UsernamePasswordToken;
	}

	// 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		// 从token中 获取用户身份信息
		String loginName = (String) token.getPrincipal();
		//
		// 以后这里需要根据username从数据库中查询,如果查询不到则返回null
		if (!loginName.equals("liubao")) {// 这里模拟查询不到
			return null;
		}

		// 数据库保存的密码,用户输入的密码还是123
		// 按照固定规则加密码结果 ,此密码 要在数据库存储,原始密码 是123,盐是haha
		String password = "01ddae4032e17a1c338baac9c4322b30";
		// 盐,随机数,此随机数也在数据库存储
		String salt = "haha";

		// 根据身份信息从数据库中查询权限数据
		List<String> roles = new ArrayList<String>();
		roles.add("role1");
		roles.add("role2");

		// ....这里使用静态数据模拟
		List<String> permissions = new ArrayList<String>();
		permissions.add("user:list");
		permissions.add("user:create");
		permissions.add("user:delete");
		
		User user = new User();
		user.setUserId("1");
		user.setLoginName(loginName);
		user.setRoles(roles);
		user.setPermissions(permissions);

		// 返回认证信息
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, password,
				ByteSource.Util.bytes(salt), getName());

		return simpleAuthenticationInfo;
	}

	// 授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// 获取身份信息
		User user = (User) principals.getPrimaryPrincipal();

		// 将权限信息封闭为AuthorizationInfo

		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

		simpleAuthorizationInfo.addStringPermissions(user.getPermissions());

		simpleAuthorizationInfo.addRoles(user.getRoles());
	

		return simpleAuthorizationInfo;
	}

}


5、登陆页面

    因为我们直接使用Shiro的登陆判断的,所以这里的登陆页面的用户名和密码的控件名必须符合命名的要求。

<BODY>
	<FORM action="login.do" method="post">

		<TABLE border="0">
			<TBODY>
				<TR>
					<TD>用户名:<input type="text" id="usercode" name="username" /></TD>
				</TR>
				<TR>
					<TD>密 码:<input type="password" id="pwd" name="password" /></TD>
				</TR>

				<TR>
					<TD align="center"><input type="submit" class="btnalink"
						value="登录" /></TD>
				</TR>
			</TBODY>
		</TABLE>
	</FORM>
</BODY>

6、登录

   因为登陆的逻辑判断交由Shiro做了,所以这里登陆逻辑就变得非常简单了。只需要判断有没有异常信息就可以了。

	// 用户登陆提交
	@RequestMapping("/login")
	public String loginsubmit(Model model, HttpServletRequest request) throws Exception {

		// shiro在认证过程中出现错误后将异常类路径通过request返回
		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
		if (exceptionClassName != null) {
			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
				throw new CustomException("账号不存在");
			} else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
				throw new CustomException("用户名/密码错误");
			} else if ("randomCodeError".equals(exceptionClassName)) {
				throw new CustomException("验证码错误");
			} else {
				throw new Exception();// 最终在异常处理器生成未知错误
			}
		}
		return "login";

	}

7、退出

    由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可,

<!-- 退出拦截,由shiro拦截并实现,所以你不需要自己在mvc配置这样的一个方法了。 -->
/logout.do = logout

    所以这个退出可以直接使用。

	<a href="logout.do">退出</a>


9、shiro过虑器总结


过滤器简称 对应的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没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

注:

anon,authcBasic,auchc,user是认证过滤器,

perms,roles,ssl,rest,port是授权过滤器