shiro入门

常见的权限控制方式

      URL拦截权限控制,底层基于拦截器或者过滤器实现

     

      方法注解权限控制,底层基于代理技术实现,为Action创建代理对象,由代理对象进行权限校验

        

用户-角色-权限:权限限定了操作,角色是权限的集合,方便权限管理,用户授予了什么角色,拥有其角色的权限集合

apache shiro框架简介

*官网:shiro.apache.org

*下载

*shiro框架的核心功能:

     认证
     授权
     会话管理
     加密

*shiro框架认证流程

    

     Application Code:应用程序代码,由开发人员负责开发的
     Subject:框架提供的接口,代表当前用户对象
     SecurityManager:框架提供的接口,代表安全管理器对象
     Realm:可以开发人员编写,框架也提供一些,类似于DAO,用于访问权限数据

*在项目中应用shiro框架进行认证

第一步:引入shiro框架相关的jar

<!-- 引入shiro框架的依赖 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-all</artifactId>
	<version>1.2.2</version>
</dependency>

第二步:在web.xml中配置spring框架提供的用于整合shiro框架的过滤器

	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>	
	</filter-mapping>

第三步:在spring配置文件中配置bean,id为shiroFilter

         *这里设置了访问资源所需要的权限,有权限才可以继续执行

    <!-- 使用shiro安全认证框架 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 注入安全管理器对象 -->
		<property name="securityManager" ref="securityManager"/>
		<!-- 注入相关页面访问URL -->
		<property name="loginUrl" value="/login.jsp"/>
		<property name="successUrl" value="/index.jsp"/>
		<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
		<!--注入URL拦截规则 -->
		<property name="filterChainDefinitions">
			<value>
				/css/** = anon
				/js/** = anon
				/images/** = anon
				/validatecode.jsp* = anon
				/login.jsp = anon
				/userAction_login.action = anon
				/page_base_staff.action = perms["staff-list"]
				/* = authc
			</value>
		</property>
	</bean>

框架提供的过滤器

        

第四步:配置安全管理器    ↑ 引用的注意名字

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

第五步:设置认证和授权的类。BOSRealm.java

认证是登录时候调用的方法

授权是访问资源之前进行的操作

package com.pb.service.realm;

public class BOSRealm extends AuthorizingRealm{
	@Autowired
	private IUserDao userDao;
	
	//认证方法
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("自定义的realm中认证方法执行了。。。。");
		UsernamePasswordToken passwordToken = (UsernamePasswordToken)token;
		//获得页面输入的用户名
		String username = passwordToken.getUsername();
		//根据用户名查询数据库中的密码
		TUser user = userDao.findUserByUsername(username);
		if(user == null){
			//页面输入的用户名不存在
			return null;
		}
		//简单认证信息对象
		AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
		//框架负责比对数据库中的密码和页面输入的密码是否一致
		return info;
	}

	//授权方法
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.addStringPermission("staff-list");
		//TODO 后期需要去数据库查询权限信息。。
		TUser user=(TUser) SecurityUtils.getSubject().getPrincipal();
		TUser user2=(TUser) principals.getPrimaryPrincipal();
		System.out.println(user==user2);
		return info;
	}
}

第六步:注册认证授权类 ↑ 引用的注意 id 值

<!-- 注册realm -->
<bean id="bosRealm" class="com.pb.service.realm.BOSRealm"></bean>

第七步:修改我们自定义的方法,进行登录权限认证

	public String login(){
		//从Session中获取生成的验证码
		String validatecode = (String) ServletActionContext.getRequest().getSession().getAttribute("key");
		//校验验证码是否输入正确
		if(StringUtils.isNotBlank(checkcode) && checkcode.equals(validatecode)){
			//输入的验证码正确
			//TUser user = userService.login(model);
			//if(user != null){
				//登录成功,通过安全框架进行认证
				Subject subject = SecurityUtils.getSubject();
				UsernamePasswordToken token = new UsernamePasswordToken(model.getUsername(),MD5Utils.md5(model.getPassword()));
				subject.login(token);
				TUser user2 = (TUser) subject.getPrincipal();
				//将user对象放入session,跳转到首页
				ServletActionContext.getRequest().getSession().setAttribute("loginUser", user2);
				return HOME;
			//}else{
				//登录失败,,设置提示信息,跳转到登录页面
				//输入的验证码错误,设置提示信息,跳转到登录页面
				//this.addActionError("用户名或者密码输入错误!");
				//return LOGIN;
			//}
		}else{
			//输入的验证码错误,设置提示信息,跳转到登录页面
			this.addActionError("输入的验证码错误!");
			return LOGIN;
		}
	}

第八步:在spring配置文件中开启shiro注解支持

	<!-- 开启shiro框架注解支持 -->
	<bean id="defaultAdvisorAutoProxyCreator" 
		class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
			<!-- 必须使用cglib方式为Action对象创建代理对象 -->
		<property name="proxyTargetClass" value="true"/>
	</bean>
	<!-- 配置shiro框架提供的切面类,用于创建代理对象 -->
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>

第九步:在Action的方法上使用shiro注解,设置访问的权限

	@RequiresPermissions("staff-list")
	public String pageQuery() throws IOException{
		staffService.pageQuery(pageBean);
		String[] excludes=new String[]{"currentPage","pageSize","detachedCriteria","qpWorkbills","qpNoticebills","qpNoticebills_1","bcDecidedzones","qpWorkbills_1","bcDecidedzones_1"};
		javaToJson(pageBean, excludes);
		return NONE;
	}

第十步:在struts.xml中配置全局异常捕获,当shiro框架抛出权限不足异常时,跳转到权限不足提示页面

<!-- 全局结果集定义 -->
<global-results>
	<result name="login">/login.jsp</result>
	<result name="unauthorized">/unauthorized.jsp</result>
</global-results>

<global-exception-mappings>
	<exception-mapping result="unauthorized" 
		exception="org.apache.shiro.authz.UnauthorizedException"></exception-mapping>
</global-exception-mappings>

第十一步:可以在页面上通过shiro标签来控制页面标签的显示

导入标签库
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
进行显示控制
<shiro:hasPermission name="staff-delete">
{
	id : 'button-delete',
	text : '作废',
	iconCls : 'icon-cancel',
	handler : doDelete
},
</shiro:hasPermission>

补充:代码级别的权限控制(基于代理技术实现)没有权限抛没有权限异常然后跳到全局结果异常页面

Subject subject = SecurityUtils.getSubject();
subject.checkPermission("staff-list");

注:web.xml中配置锅过滤器的时候,放在struts过滤器的前边。否则会报一个无效的异常。

遇到的异常

22:26:26,807  INFO AuthorizingRealm:248 - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
true
22:26:26,827  INFO AuthorizingRealm:248 - No cache or cacheManager properties have been set.  Authorization cache cannot be obtained.
true
22:26:27,355 ERROR DefaultDispatcherErrorHandler:42 - Exception occurred during processing request: null
java.lang.NullPointerException
    at com.company.bos.action.StaffAction.findAll(StaffAction.java:27)

① debug看一下为什么空指针了,但是却出现的下面的这个弹窗。弹了好几遍之后还是跳了debug发现,service的自动

注入是null,所以报的空指针。

如果我们开启了,shiro注解,会和@Autowired冲突。手动配置一下。奇怪的是以前的时候,没这个问题,现在报错了,可能是和具体的代码实现还有一定的联系

参考文档:https://www.cnblogs.com/ToddleLobster/articles/7941624.html

a. 给UserServiceImpl指定id @Service("userService");
b. 然后给UserAction中的userService字段加入setter方法。
c. 如上处理后,采用@Autowire@Resource注入userService即可。

异常二

org.apache.shiro.authc.AuthenticationException: Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - e, rememberMe=false].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).

Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:604)
    at java.util.ArrayList.get(ArrayList.java:382)

下边的这个好解决,是因为逻辑判断的问题。这里的 || 写成了 && ,但是原先可以认证,为什么现在失败了。。 

		if(userList == null || userList.size()<=0){
			//页面输入的用户名不存在
			return null;
		}

改过来后,出现了新的异常

org.apache.shiro.authc.UnknownAccountException: Realm [com.company.bos.service.BOSRealm@6b95b708] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - e, rememberMe=false].

分析:这个错误其实是由认证代码引起的,在登录信息和数据库信息匹配失败的时候,return null所引起的异常,在action中进行了异常捕获。发生异常返回登录页面就可以。

	//认证方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("自定义的realm中认证方法执行了。。。。");
		UsernamePasswordToken passwordToken = (UsernamePasswordToken)token;
		//获得页面输入的用户名
		String username = passwordToken.getUsername();
		//根据用户名查询数据库中的密码
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
		detachedCriteria.add(Restrictions.eq("username", username));
		List<User> userList = userDao.findByCriteria(detachedCriteria);
		if(userList == null || userList.size()<=0){
			//页面输入的用户名不存在
			return null;
		}
		User user=userList.get(0);
		//简单认证信息对象
		AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
		//框架负责比对数据库中的密码和页面输入的密码是否一致
		return info;
	}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值