参考:http://hnbcjzj.iteye.com/blog/1706600
简介: Shiro 是一个 Apache Incubator 项目,旨在简化身份验证和授权。是一个很不错的安全框架。
借用别人写的一个shiro框架的实例来对shiro的应用做下记录。
1.基本概念
首先理解两个概念:认证和授权
我开始接触权限的时候,并没有明确区分认证和授权,把这两个概念混为一团,导致在开始运用shi'ro的时候有些地方没有理解透彻。
认证,简单来说,就是指用户身份是否合法,通俗的理解就是用户是否是登录用户,登录的用户为认证用户,为登录的用户为非认证用户。
授权,一般来说,认证是第一步,用户经过认证以后,只能说明用户是系统的合法用户,但是权限是分级别的,用户身份合法并不代表用户就对所有资源拥有操作权限,要对某一个资源操作,还需要相应的权限管理,这就是授权要就解决的问题。
2.配置
结合DEMO进行说明。
先将shiro引入web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value>
</context-param>
<!-- apache 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>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
shiro的配置文件是spring-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" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<description>Shiro 配置</description>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.jsp" />
<property name="successUrl" value="/login.jsp" />
<property name="unauthorizedUrl" value="/error/noperms.jsp" />
<property name="filterChainDefinitions">
<value>
/login.jsp* = anon
/login.do* = anon
/index.jsp*= anon
/error/noperms.jsp*= anon
/*.jsp* = authc
/*.do* = authc
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm -->
<property name="realm" ref="monitorRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!--自定义Realm 继承自AuthorizingRealm -->
<bean id="monitorRealm" class="com.shiro.service.MonitorRealm"></bean>
<!-- securityManager -->
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<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>
</beans>
在这个配置文件中有两个重点要关注的地方:
1)过滤器配置。在里面有两个类型的过滤器配置,anon和authc,anon,要注意的是anon必须配置在authc配置项的前面,否则anon配置项会失效。
Anon:不指定过滤器,不错是这个过滤器是空的,什么都没做,跟没有一样。
Authc:验证,这些页面必须验证后才能访问,也就是我们说的登录后才能访问。
简单来讲,anon就是指所有用户(匿名用户)都可以访问的资源,authc是指认证用户(登录用户)才可以访问的资源。
这里还有其他的过滤器,我没用,比如说授权,这个比较重要,但是这个过滤器有个不好的地方,就是要带一个参数,所以如果配在这里就不是很合适,因为每个页面,或是.do的权限不一样,而我们也没法事先知道他需要什么权限。所以这里不配,我们在代码中再授权。这里.do和.jsp后面的*表示参数,比如login.jsp?main这种,是为了匹配这种。好行了,继续往下吧。
2)自定义realm
realm的作用就是自己来定义认证和授权的业务逻辑。可以灵活的控制权限。这里自定义了一个monitorRealm用来自己写权限业务,这个后面慢慢说明。
3.认证
现在先看第一步认证。
打开登录页面,输入用户名/密码点击提交,就会提交到后台的登录actIon的方法:
@RequestMapping(params = "main")
public ModelAndView login(User user,HttpSession session, HttpServletRequest request) {
ModelAndView modelView = new ModelAndView();
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(
user.getUsercode(), EncryptUtils.encryptMD5(user.getPassword()));
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (AuthenticationException e) {
modelView.addObject("message", "login errors");
modelView.setViewName("/login");
e.printStackTrace();
}
if(currentUser.isAuthenticated()){
user.setUserName("张三");
session.setAttribute("userinfo", user);
modelView.setViewName("/main");
}else{
modelView.addObject("message", "login errors");
modelView.setViewName("/login");
}
return modelView;
}
这里有两条语句需要说明:
Subject currentUser = SecurityUtils.getSubject() ;
currentUser就是代表当前的用户。
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsercode(),EncryptUtils.encryptMD5(user.getPassword()));
token大家叫他令牌,也就相当于一张表格,你要去验证,你就得填个表,里面写好用户名密码,交给公安局的同志给。
currentUser.login(token);
这个就是校验token,校验过程会回调realm里面的认证方法:
protected AuthenticationInfo doGetAuthenticationInfo()
这里就是自己写认证逻辑的地方,看下这里的业务
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
/* 这里编写认证代码 */
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// User user = securityApplication.findby(upToken.getUsername());
User user = new User();
user.setUsercode(token.getUsername());
user.setUserName("admin");
user.setPassword(EncryptUtils.encryptMD5("admin"));
// if (user != null) {
return new SimpleAuthenticationInfo(user.getUserName(),
user.getPassword(), getName());
}
是否调用这里是在前面的配置文件中配置的,我们前面spring里配了这个
/*.jsp* = authc
/*.do* = authc
你配了authc过滤器,shiro会自动调currentUser.isAuthenticated()这个方法,没有登录的将被返回
<property name="unauthorizedUrl" value="/error/noperms.jsp" />
配置的页面。
调用这个认证方法后,会对用户的用户名密码进行校验,校验失败跳转到登录页面,校验成功处理登录方法后的逻辑。这里是跳转到main主页。
4.授权
再看第二步,授权
再main页面上点击myjsp,会提交到user.do?myjsp,看下后台的这个方法:
@RequestMapping(params = "myjsp")
public String home() {
Subject currentUser = SecurityUtils.getSubject();
if(currentUser.isPermitted("user.do?myjsp")){
return "my";
}else{
return "error/noperms";
}
}
这里
Subject currentUser = SecurityUtils.getSubject();
currentUser.isPermitted("user.do?myjsp");
这两行语句的意思是判断当前用户是否有访问user.do?myjsp的权限
调用currentUser.isPermitted("user.do?myjsp");此方法后会回调realm中的授权验证方法
protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals)
看下这里是如何授权的:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
/* 这里编写授权代码 */
Set<String> roleNames = new HashSet<String>();
Set<String> permissions = new HashSet<String>();
roleNames.add("admin");
permissions.add("user.do?myjsp");
permissions.add("login.do?main");
permissions.add("login.do?logout");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
这里没有用到数据库,直接将权限硬编码在代码里面,添加admin角色,然后给角色添加权限,当然正常肯定是预先存放在数据库里面,直接从数据库里面查询出来,构造成一个权限对象返回,再与currentUser.isPermitted()中传入的权限参数进行对比,看看用户是否有访问此地址参数的权限,没有权限就返回到没有权限的页面。
5.总结
通过前面的demo流程,可以分析出如下几点:
1)认证的配置一般都是配置再shiro的配置文件,而授权设置比较灵活,一般不直接写在shiro配置文件里面。
2)认证是授权的前提,通过通过了认证,才会用相应的权限管理。
3)认证过程会调用reanlm里面的认证方法doGetAuthenticationInfo(),授权会调用realm里面的授权方法doGetAuthenticationInfo()
一般认证方法只会在登录的时候调用一次,而授权则在需要授权的地方都会调用,会被多次调用。