一.权限概述

1.权限控制的两个核心概念:

认证:系统提供的用于识别用户身份的功能,通常登录功能就是认证功能-----让系统知道你是谁??

授权:系统授予用户可以访问哪些功能的许可(证书)----让系统知道你能做什么??

2.常见的权限控制方式


URL拦截权限控制,底层基于拦截器或者过滤器实现,原理图如下:


springsecurity整合jwt 权限控制 spring权限控制框架_shiro


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

springsecurity整合jwt 权限控制 spring权限控制框架_spring_02

二.shiro框架的简介

Apache的shiro框架是一个灵活且强大的开源安全权限管理框架,能够处理身份认证,授权,企业会话管理和加密等工作。

下图是shiro具体的功能点:

springsecurity整合jwt 权限控制 spring权限控制框架_shiro_03


Shiro的4大部分——身份验证,授权,会话管理和加密



•·  Authentication:身份验证,简称“登录”。



•·  Authorization:授权,给用户分配角色或者权限资源



•·  Session Management:用户session管理器,可以让CS程序也使用session来控制权限



•·  Cryptography:把JDK中复杂的密码加密方式进行封装。



除了以上功能,shiro还提供很多扩展



•·  Web Support:主要针对web应用提供一些常用功能。



•·  Caching:缓存可以使应用程序运行更有效率。



•·  Concurrency:多线程相关功能。



•·  Testing:帮助我们进行测试相关功能



•·  "Run As":一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。



•·  “Remember Me”:记住用户身份,提供类似购物车功能。



三.shiro的访问流程

springsecurity整合jwt 权限控制 spring权限控制框架_shiro_04

application code:指用户自己的应用代码,在web应用中,指的一般是控制层代码


Subject:与程序进行交互的对象,可以是人也可以是服务或者其他,通常就理解为当前用户。所有Subject实例都必须绑定到一个SecurityManager上。我们与一个Subject交互,运时shiro会自动转化为与SecurityManager交互的特定subject的交互。

SecurityManager是 Shiro的核心,初始化时协调各个模块运行。然而,一旦SecurityManager协调完毕,SecurityManager会被单独留下,且我们只需要去操作Subject即可,无需操作SecurityManager。但是我们得知道,当我们正与一个 Subject进行交互时,实质上是SecurityManager在处理Subject安全操作。

Realms: Shiro中作为应用程序和安全数据之间的“桥梁”或“连接器”。他获取安全数据来判断subject是否能够登录,subject拥有什么权限。他有点类似DAO。在配置realms时,需要至少一个realm。而且Shiro提供了一些常用的Realms来连接数据源,如LDAP数据源的JndiLdapRealm,JDBC数据源的JdbcRealm,ini文件数据源的IniRealm,properties文件数据源的PropertiesRealm,等等。我们也可以插入自己的Realm实现来代表自定义的数据源。像其他组件一样,Realms也是由SecurityManager控制,属于securitymanager的一部分。


secritymanager的其他重要部分:

1.Authenticator(org.apache.shiro.authc.Authenticator):登录控制器,主要用来管理shiro的认证功能。

2.Authorizer(org.apache.shiro.authz.Authorizer):授权控制器,决定subject能拥有什么样角色或者权限。

3.CacheManager(org.apahce.shiro.cache.CacheManager):缓存管理器,可以减少不必要的后台访问。提高应用效率,增加用户体验。



四.使用shiro与spring整合完成认证功能(基于maven,eclipse)


第一步:引入shiro的maven依赖

第二步:在web.xml中配置shiro的过滤器,注意过滤器配置要放在前面


<!-- 配置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>


此时启动服务器,会报一个不存在shiroFilter的bean对象异常,故在spring配置文件中配置

第三步:在spring配置文件中中配置一个ShiroFilterFactoryBean,id为shiroFilter,在这个工厂对象中还需要指定认证授权成功或者失败后访问的URL,需要注入安全管理器对象,和URL级别的拦截规则。


<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>


第四步:配置安全管理器,安全管理器需要注入一个Realm对象,在稍后定义。

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"/>


第五步:编写login方法,使用shiro的方法进行认证操作,只需要构造包含用户名密码的令牌对象,再调用subject(当前对象)的login方法,把令牌对象交由realm处理即可,shiro处理认证请求的方式是从数据库中找到登陆用户的密码,再与令牌密码比对。


public String login() {
        //从Session中获取生成的验证码
        String validateCode = (String) ServletActionContext.getRequest().getSession().getAttribute("key");
        //校验验证码是否输入正确
        if (StringUtils.isNotBlank(validateCode) && checkcode.equals(validateCode)) {
            //输入的验证码正确,使用shiro的方式认证
        	//获得当前对象
        	Subject subject=SecurityUtils.getSubject();
        	//生成令牌对象,传递到realm中
           AuthenticationToken token=new UsernamePasswordToken(model.getUsername(),MD5Utils.md5(model.getPassword()));
           try {
        	   subject.login(token);
		} catch (Exception e) {
			e.printStackTrace();
			return LOGIN;
		}
           //登陆成功,获取登陆对象存到session中
          User user=(User) subject.getPrincipal();
          ServletActionContext.getRequest().getSession().setAttribute("loginUser",user);
          return HOME;
          
        } else {
            //输入的验证码错误,设置提示信息,跳转到登录页面
            this.addActionError("验证码不正确,请再输一次");
            return LOGIN;
        }

    }

第六步:自定义一个Realm对象,继承AuthorizingRealm抽象类,实现认证和授权方法。



public class BOSRealm extends AuthorizingRealm{
	@Autowired
	private UserDao userDao;
	/* 
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken passwordToken=(UsernamePasswordToken) token;
		//从令牌中获得用户名,查询相应密码
		String userName=passwordToken.getUsername();
		User user=userDao.findUserByUsername(userName);
		if(user==null)
		{
			return null;
		}
		AuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
		return authenticationInfo;
	}
}

第七步:在spring配置文件中配置自定义Realm的bean,并注入到securitymanager中



<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="bosRealm"/>
	</bean>
	<bean id="bosRealm" class="com.cai.bos.realm.BOSRealm"/>



综上七步,完成shiro与spring整合的完成认证工作的所有内容。需要在spring中配置的shiro相关的bean有:

ShiroFilterFactoryBean, DefaultWebSecurityManager,自定义Realm,并且注入关系为:自定义Realm-->DefaultWebSecurityManager-->ShiroFilterFactoryBean。可以清晰地看到,shiro帮助我们完成大部分的工作,我们只需要完成Realm自定义认证逻辑即可。