一.权限概述
1.权限控制的两个核心概念:
认证:系统提供的用于识别用户身份的功能,通常登录功能就是认证功能-----让系统知道你是谁??
授权:系统授予用户可以访问哪些功能的许可(证书)----让系统知道你能做什么??
2.常见的权限控制方式
URL拦截权限控制,底层基于拦截器或者过滤器实现,原理图如下:
方法注解权限控制,底层基于代理技术实现,为action创建代理对象,由代理对象进行权限校验,原理图如下:
二.shiro框架的简介
Apache的shiro框架是一个灵活且强大的开源安全权限管理框架,能够处理身份认证,授权,企业会话管理和加密等工作。
下图是shiro具体的功能点:
Shiro的4大部分——身份验证,授权,会话管理和加密
•· Authentication:身份验证,简称“登录”。
•· Authorization:授权,给用户分配角色或者权限资源
•· Session Management:用户session管理器,可以让CS程序也使用session来控制权限
•· Cryptography:把JDK中复杂的密码加密方式进行封装。
除了以上功能,shiro还提供很多扩展
•· Web Support:主要针对web应用提供一些常用功能。
•· Caching:缓存可以使应用程序运行更有效率。
•· Concurrency:多线程相关功能。
•· Testing:帮助我们进行测试相关功能
•· "Run As":一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。
•· “Remember Me”:记住用户身份,提供类似购物车功能。
三.shiro的访问流程
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自定义认证逻辑即可。