shiro使用教程

生产环境

  • shiro版本: 1.2.3
  • spring版本L 4.0

shiro的基础

  shiro是一个权限框架,简单粗暴易用,主要有几个部分组成:

* SecurityManager:    shiro的管理器
* UserRealm:          shiro的用户域,用户身份验证跟授权都在这块
* ShiroFilter:        shiro的拦截器,用于拦截url请求

  在生产环境中主要配的也就是这三块地方,接下来我们来看下每一块的详细配置

shiro的用户域UserRealm

  用户域主要做做两件事情,一件是身份验证(登录),另一件是用户授权,可以看下下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
	
public class UserRealm extends AuthorizingRealm {

@Resource
private UserService userService;

/**
* @description 验证角色、权限
* @author wchuang
* @time 2016年7月5日 下午5:32:32
*/

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String account = (String)super.getAvailablePrincipal(principals);

SimpleAuthorizationInfo authorizationInfo = (SimpleAuthorizationInfo) CacheHelper.get(account); //判断是否存在缓存中
if(authorizationInfo == null){
List<String> roleStr = new ArrayList<String>();
List<Role> roleList = userService.getRolesByAccount(account);
if(null != roleList && roleList.size() > 0){
for(Role role : roleList){
roleStr.add(role.getName());
}
}

authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(roleStr);
CacheHelper.put(account, authorizationInfo,10*60); //授权成功后,添加到缓存中
}

return authorizationInfo;
}

/**
* @description 验证授权、登录
* @author wchuang
* @time 2016年7月5日 下午5:32:32
*/

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
User user = userService.getUserByAccount(token.getUsername());
if(null != user){
AuthenticationInfo authInfo = new SimpleAuthenticationInfo(user.getAccount(), user.getPassword(), "userRealm");
this.setSession("currentUser", user);
return authInfo;
}
return null;
}

private void setSession(Object key, Object value){
Subject currentUser = SecurityUtils.getSubject();
if(null != currentUser){
Session session = currentUser.getSession();
if(null != session){
session.setAttribute(key, value);
}
}
}

}

  首先定义自己的用户域,继承shiro提供的AuthorizingRealm类,然后实现里面的两个方法,前者是授权,后者是验证,我们先看后者,也就是身份验证,我们可以看到传进来的是一个AuthenticationToken,这是shiro封装的用户信息

  • 我们第一步是将他转成简单的账号/密码的token,然后获取账号
  • 接着从数据库里面查记录,看有没有这个账号的用户
  • 如果这个用户存在,那么就创建一个身份验证信息AuthenticationInfo,可以看到传进去的参数分别是账号、密码、用户域字符串,然后返回
  • 那么密码的校验在那里呢?shiro帮我们完成
  • 那么什么时候发生这个身份验证的操作呢?这个我们可以看下下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private void authLogin(String account, String password) {

    UsernamePasswordToken token = new UsernamePasswordToken(account, password);
    Subject subject = SecurityUtils.getSubject();
    subject.login(token);
    if (subject.isAuthenticated()) {
    token.clear();
    }
    }

      我们可以看到,假设传进来的是用户表单提交过来的用户名跟密码,我们做仅是根据这两个参数去创建一个用户信息,然后调用Subject实体的login()方法就可以进到刚才的操作了,简单粗暴,那么可能有人会想到,用户的密码是加密后存到数据库的,比如MD5,这种情况shiro是支持配置密码的校验规则的,我们来看下下面的配置(基于spring)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <bean id="userRealm" class="com.distinct.benefit.user.realm.UserRealm">
    <property name="credentialsMatcher">
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    <property name="hashAlgorithmName" value="MD5" /><!-- 以MD5方式比较 -->
    </bean>
    </property>

    </bean>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="userRealm" />
    </bean>

    <!-- Shiro Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <!-- 设定用户的登录链接,这里为cas登录页面的链接地址可配置回调地址 -->
    <property name="loginUrl" value="/login.jsp" />
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    <property name="filterChainDefinitions">
    <value>
    /login = anon
    /manage/** = user
    /doctor/** = user
    /user/** = user
    /invoice/** = user
    </value>
    </property>
    </bean>


    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>

      可以看到我们定义了一个UserRealm类,然后配置了credentialsMatcher属性,他的值给的就是MD5方式的校验,里面还有几个其他的类,我们展开来说

  • SecurityManager: 我采用的是默认提供的,然后只是需要往里面传了用户域userRealm,另外sessionManger、cacheManager也要在这里设值(如果需要的话)
  • shiroFilter: shiro拦截器,这个我们就展开说一下

shiro拦截器

  • 首先我们需要配置登录的url
  • 接着是授权失败的界面
  • 最后这个最重要,这个用来配置具体的url需要怎么样的角色跟权限的,其中anon表示是无需验证,一般登录界面用这个,然后系统中的其他请求资源就需要我们根据自己的需求去做定制了,我这里设置的是需要用户登录user

web.xml文件配置

  最后有一点需要注意的是,需要在web.xml文件中真正去配置shiro的拦截器filter,请看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
<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>/*</url-pattern>
</filter-mapping>

  其中有一点需要注意的是这个filter的name一定一定要跟上面的bean的id是一样的