shiro深入浅出

一,介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。     ----- 百度百科

shiro是一个安全框架。可以进行认证,授权,密码加密,回话管理等

二,主要功能

主要功能可以用下面的一张图来概括

Subject主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;

SessionManager如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);

SessionDAODAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

CacheManager缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

Cryptography密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

三,详细介绍

结合经管项目,详细介绍shiro的使用

经管项目shiro的使用是结合MVC使用的,主要用到了其认证,授权,会话管理的功能

1.在web.xml加入shiro的过滤器

1

2

3

4

5

6

7

8

9

10

11

12

13

<filter>

    <filter-name>shiroFilter</filter-name>

    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

    <async-supported>true</async-supported>

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

2.配置shiro的过滤器【这是经管系统的配置,我加了一些注释,助于理解】

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

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

//shiro过滤器

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

   <property name="securityManager" ref="securityManager"/>

    //登录页面

   <property name="loginUrl" value="/web/user/login"/>

    //自定义的拦截器

   <property name="filters">

      <util:map>

         <entry key="authc" value-ref="formAuthenticationFilter"/>

         <entry key="sysUser" value-ref="sysUserFilter"/>

         <entry key="ajaxUser" value-ref="ajaxUserFilter"/>

         <entry key="logout" value-ref="systemLogoutFilter" />

         <entry key="weixin" value-ref="weixinPermissionFilter" />

      </util:map>

   </property>

    //自定义的拦截策略

   <property name="filterChainDefinitions">

      <value>

         /services/** = anon   //不做认证

         /PacServletCp.do = anon   //不做认证

         /remoteAuth/canAccess = anon   //不做认证

         /favicon.ico  = anon   //不做认证

         /resources/js/pjs/** = anon   //不做认证

         /resources/** = anon   //不做认证

         /web/user/login = authc   //登录拦截

         /logout = logout   //注销拦截

         /authenticated = authc   //登录拦截

         /**/**/getOADetail = anon   //不做认证

         /ajax/monthlyDetail/exportDetail = anon   //不做认证

         /weixin/** = weixin   //微信页面拦截

         /** = ajaxUser, sysUser   //其他请求的拦截

      </value>

   </property>

</bean>

<!-- 基于Form表单的身份验证过滤器 -->

<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">

    //其实这里的配置信息可以省略,因为其内部默认就是这些值

   <property name="usernameParam" value="username"/>

   <property name="passwordParam" value="password"/>

   <property name="rememberMeParam" value="rememberMe"/>

</bean>

//自定义拦截器

<bean id="sysUserFilter" class="com.best.scop.interceptor.SysUserFilter"/>

//自定义拦截器

<bean id="ajaxUserFilter" class="com.best.scop.interceptor.AjaxUserFilter"/>

//自定义拦截器

<bean id="systemLogoutFilter" class="com.best.scop.interceptor.SystemLogoutFilter"/>

//自定义拦截器

<bean id="weixinPermissionFilter" class="com.best.scop.interceptor.WeixinPermissionFilter" />

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

    //相当于数据库,里面有认证策略

   <property name="realm" ref="myShiroRealm"/>

    //session管理器

   <property name="sessionManager" ref="sessionManager"/>

    //cookie管理

   <property name="rememberMeManager" ref="rememberMeManager"/>

</bean>

<!-- Realm实现 -->

<bean id="myShiroRealm" class="com.best.scop.interceptor.MyShiroRealm">

    //真正做认证操作的代码

   <property name="credentialsMatcher" ref="ldapAndDbCredentialsMatcher"/>

    //是否做缓存

   <property name="cachingEnabled" value="false"/>

</bean>

<bean id="ldapAndDbCredentialsMatcher" class="com.best.scop.interceptor.LadpAndDbCredentialMatcher" />

<!-- 会话管理器 -->

<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">

    //定义的是全局的session会话超时时间,此操作会覆盖web.xml文件中的超时时间配置

   <property name="globalSessionTimeout" value="1800000"/>

    //删除所有无效的Session对象,此时的session被保存在了内存里面

   <property name="deleteInvalidSessions" value="true"/>

    //需要让此session可以使用该定时调度器进行检测

   <property name="sessionValidationSchedulerEnabled" value="true"/>

    //定义要使用的无效的Session定时调度器

   <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>

    //定义Session可以进行序列化的工具类

   <property name="sessionDAO" ref="sessionDAO"/>

    //定义sessionIdCookie模版可以进行操作的启用

   <property name="sessionIdCookieEnabled" value="true"/>

    //所有的session一定要将id设置到Cookie之中,需要提供有Cookie的操作模版

   <property name="sessionIdCookie" ref="sessionIdCookie"/>

    //session监听器【经管项目没有实现这个功能】

   <property name="sessionListeners" ref="sessionListeners"/>

</bean>

<!-- 会话验证调度器 -->

<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">

    //触发的时间间隔

   <property name="sessionValidationInterval" value="1800000"/>

    //session管理器

   <property name="sessionManager" ref="sessionManager"/>

</bean>

<!-- 会话DAO -->

<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">

    //session缓存的名字

   <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>

    //生成session id的方法

   <property name="sessionIdGenerator" ref="sessionIdGenerator"/>

</bean>

<!-- 会话ID生成器 -->

<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

<!-- 会话Cookie模板 -->

<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">

    //cookie中session的叫法

   <constructor-arg value="sid"/>

    //只支持http,保证该系统不会受到跨域的脚本攻击

   <property name="httpOnly" value="true"/>

    //定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失

   <property name="maxAge" value="-1"/>

</bean>

<bean id="sessionListeners" class="java.util.ArrayList">

   <constructor-arg>

      <list>

         <ref bean="simpleDbSessionListener"/>

      </list>

   </constructor-arg>

</bean>

<bean id="simpleDbSessionListener" class="com.best.scop.interceptor.SimpleDbSessionListener" />

<!-- rememberMe管理器 -->

<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">

   <!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->

   <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdog==')}"/>

   <property name="cookie" ref="rememberMeCookie"/>

</bean>

<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">

   <constructor-arg value="rememberMe"/>

    //防止跨域脚本攻击

   <property name="httpOnly" value="true"/>

    //记住我cookie生效时间,默认30天 ,单位秒

   <property name="maxAge" value="259200"/>

</bean>

//管理shiro bean生命周期

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

3.开启注解

1

2

3

4

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">

   <property name="securityManager" ref="securityManager"/>

</bean>

常用的注解:

RequiresRoles :当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常
RequiresPermissions :当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresAuthentication :使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证。
RequiresUser :当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
RequiresGuest:使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。

RequiresPermissions 注解是经管项目中使用最多的注解

RequiresPermissions 源码

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package org.apache.shiro.authz.annotation;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface RequiresPermissions {

 

    /**

     * The permission string which will be passed to {@link org.apache.shiro.subject.Subject#isPermitted(String)}

     * to determine if the user is allowed to invoke the code protected by this annotation.

     */

    String[] value();

     

    /**

     * The logical operation for the permission checks in case multiple roles are specified. AND is the default

     * @since 1.1.0

     */

    Logical logical() default Logical.AND;

 

}

其中value属性就是权限的名称,logical属性【如果有多个权限,那么AND表示是并且的关系,OR表示是或者的关系,默认是AND】

 

4.java代码

realm,这是经管项目的realm

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

package com.best.scop.interceptor;

 

import com.best.scop.exception.biz.AuthException;

import com.best.scop.service.user.UserService;

import com.best.scop.util.auth.SecurityContextHolder;

import com.best.scop.vo.user.UserVO;

import org.apache.shiro.authc.*;

import org.apache.shiro.authz.AuthorizationException;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.springframework.beans.factory.annotation.Autowired;

 

import java.util.Set;

 

/**

 * Created by bl00157 on 2016/3/2.

 */

public class MyShiroRealm extends AuthorizingRealm {

 

    @Autowired

    private UserService userService;

 

    @Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        UserVO user = SecurityContextHolder.getContext().getUser();

        if(user==null) {

            throw new AuthorizationException((new AuthException(AuthException.SESSION_EXPIRED)).getMessage());

        }

        //reload user?

        //user = userService.get(user.getId());

        Set<String> roles = user.getRoleSetString();

        Set<String> permissions = user.getPermissionSetString();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        authorizationInfo.setRoles(roles);

        authorizationInfo.setStringPermissions(permissions);

        return authorizationInfo;

    }

 

    @Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

 

        String loginName = (String)token.getPrincipal();

        if (loginName==null || loginName.trim().length()<1) {

            throw new IncorrectCredentialsException();

        }

        UserVO vo = userService.findByLoginName(loginName);

        if (vo == null) {

            throw new IncorrectCredentialsException();

        }

        if(vo.getDisabled()){

            throw new DisabledAccountException();

        }

        // 交给 AuthenticatingRealm 使用 CredentialMatcher 进行密码匹配

        return new SimpleAuthenticationInfo(vo.getLoginName(), vo, getName());

    }

 

}

 

在以上配置都配置好了之后,当我们做登录操作的时候,首先会调用doGetAuthenticationInfo()方法去认证登录的用户名和密码,会去配置的ldapAndDbCredentialsMatcher做真正的密码认证。

认证成功之后,会把这个用户存下来,保存在shiro的session中,并且会去查找这个用户相关的角色信息与权限信息。

然后调用doGetAuthorizationInfo()方法进行授权,给这个登陆的用户授权【根据在数据库中查到的角色与权限信息】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值