shiro学习笔记

1.shiro介绍

1.1介绍

shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。

shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。

使用shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本。

1.2shrio功能特点:

1) Authentication:身份认证/登录,验证用户是不是拥有相应的身份。

2) Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。

3) Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的。

4) Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。

5) Web Support:Web支持,可以非常容易的集成到 web 环境。

6) Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。

7) Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。

8) Testing:提供测试支持。

9) Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。

10) Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

1.3shiro运行原理

 

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

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

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

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

5) Realm:可以有1个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的。它可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等。

6) SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 需要有人去管理它的生命周期,这个组件就是 SessionManager。而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境。

7) SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD。我们可以自定义 SessionDAO 的实现,控制 session 存储的位置。如通过 JDBC 写到数据库或通过 jedis 写入 redis 中。另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能。

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

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

1.4过滤器

当 Shiro 被运用到 web 项目时,Shiro 会自动创建一些默认的过滤器对客户端请求进行过滤。以下是 Shiro 提供的过滤器:

过滤器简称

对应的 Java

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

noSessionCreation

org.apache.shiro.web.filter.session.NoSessionCreationFilter

解释:

/admins/**=anon               # 表示该 uri 可以匿名访问

/admins/**=auth               # 表示该 uri 需要认证才能访问

/admins/**=authcBasic         # 表示该 uri 需要 httpBasic 认证

/admins/**=perms[user:add:*]  # 表示该 uri 需要认证用户拥有 user:add:* 权限才能访问

/admins/**=port[8081]         # 表示该 uri 需要使用 8081 端口

/admins/**=rest[user]         # 相当于 /admins/**=perms[user:method],其中,method 表示  get、post、delete 等

/admins/**=roles[admin]       # 表示该 uri 需要认证用户拥有 admin 角色才能访问

/admins/**=ssl                # 表示该 uri 需要使用 https 协议

/admins/**=user               # 表示该 uri 需要认证或通过记住我认证才能访问

/logout=logout                # 表示注销,可以当作固定配置

注意:

anon,authcBasic,auchc,user 是认证过滤器。

perms,roles,ssl,rest,port 是授权过滤器。

2.spring+springmvc+mybatis+shrio权限认证

2.1 pom.xml

<!-- shiro -->

        <dependency>

            <groupId>org.apache.shiro</groupId>

            <artifactId>shiro-spring</artifactId>

            <version>1.2.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.shiro</groupId>

            <artifactId>shiro-ehcache</artifactId>

            <version>1.2.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.shiro</groupId>

            <artifactId>shiro-core</artifactId>

            <version>1.2.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.shiro</groupId>

            <artifactId>shiro-web</artifactId>

            <version>1.2.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.shiro</groupId>

            <artifactId>shiro-quartz</artifactId>

            <version>1.2.3</version>

        </dependency>

2.2 web.xml配置shiro

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>

            classpath*:applicationContext.xml

            classpath*:applicationContext-shiro.xml

       </param-value>

  </context-param>

  <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

  </listener>

  <!-- 用户单会话过滤器 -->

  <!--  <filter>

    <filter-name>userSingleSessionFilter</filter-name>

    <filter-class>com.vulnverify.web.filter.UserSingleSessionFilter</filter-class>

  </filter>

   <filter-mapping>

    <filter-name>userSingleSessionFilter</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>-->

 

  <!-- 字符编码过滤器 -->

  <filter>

    <filter-name>encodingFilter</filter-name>

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

    <init-param>

      <param-name>encoding</param-name>

      <param-value>UTF-8</param-value>

    </init-param>

    <init-param>

      <param-name>forceEncoding</param-name>

      <param-value>true</param-value>

    </init-param>

  </filter>

  <filter-mapping>

    <filter-name>encodingFilter</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>

   <filter>

   <!-- shiro权限 -->

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

  <!-- log4j日志 -->

  <context-param>

    <param-name>log4jConfigLocation</param-name>

    <param-value>classpath:log4j.properties</param-value>

  </context-param>

  <context-param>

    <param-name>log4jRefreshInterval</param-name>

    <param-value>60000</param-value>

  </context-param>

  <listener>

    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

  </listener>

  <!-- springmvc DispatcherServlet -->

  <servlet>

    <servlet-name>dispatcher</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>classpath*:spring-mvc.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>dispatcher</servlet-name>

    <url-pattern>/</url-pattern>

  </servlet-mapping>

  <welcome-file-list>

    <welcome-file>/index.jsp</welcome-file>

  </welcome-file-list>

  <error-page>

    <error-code>404</error-code>

    <location>/page/404</location>

  </error-page>

  <error-page>

    <error-code>500</error-code>

    <location>/page/500</location>

  </error-page>

  <error-page>

    <exception-type>org.apache.shiro.authz.AuthorizationException</exception-type>

    <location>/page/401</location>

  </error-page>

  <error-page>

    <error-code>400</error-code>

    <location>/page/400</location>

  </error-page>

</web-app>

2.3 shrio配置文件 applicationContext-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="

       http://www.springframework.org/schema/beans classpath:schema/spring-beans.xsd

       http://www.springframework.org/schema/util classpath:schema/spring-util.xsd">

 

    <description>apache shiro配置</description>

 

     <!-- web.xml中shiro的filter对应的bean -->

    <!-- Shiro 的Web过滤器 -->

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

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

         <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证 -->

        <property name="loginUrl" value="/static/login/loginPanel/login.html"/>

        <!-- 认证成功跳转界面 -->

        <property name="successUrl" value="/static/index.html"/>

        <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面 -->

        <property name="unauthorizedUrl" value="/page/401"/>

        <property name="filterChainDefinitions">

            <value>

                <!-- 静态资源允许访问 -->

                /static/** = anon

                <!-- 登录页允许访问 -->

                /user/login = anon

                /publicKey = anon

                /page/exception = anon

                /verificationCode = anon

                <!-- 其他资源需要认证 -->

                /** = authc

            </value>

        </property>

    </bean>

 

 <!-- 安全管理器 -->

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

        <!-- 注入realm -->

        <property name="realms">

            <list>

                <ref bean="securityRealm"/>

            </list>

            </property>

        <!-- 注入缓存管理器 -->

        <property name="cacheManager" ref="shiroEhcacheManager" />

        <!-- 注入sessiong管理器 -->

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

         <!-- 记住我 -->

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

    </bean>

 

    <!-- 缓存管理器 使用Ehcache实现 -->

    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">

        <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>

    </bean>

 

    <!-- 会话DAO -->

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

   <!--  <bean id="sessionDAO" class="com.vulnverify.web.session.SessionRedisDao"/> -->

 

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

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

<!--     <bean id="sessionManager" class="com.vulnverify.web.security.WebSessionManager"> -->

       <!-- session的失效时长,单位毫秒 -->

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

        <!-- 删除失效的session -->

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

    </bean>

 

    <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID --> 

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

        <!-- cookie的name,对应的默认是 JSESSIONID --> 

        <constructor-arg name="name" value="SHAREJSESSIONID" />

        <!-- jsessionId的path为 / 用于多个系统共享jsessionId --> 

        <property name="path" value="/" /> 

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

    </bean>

      <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->

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

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

    </bean>

    <!-- 记住我cookie -->

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

        <!-- rememberMe是cookie的名字 -->

        <constructor-arg value="rememberMe" />

        <!-- 记住我cookie生效时间30天 -->

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

    </bean>

  

    <!-- 自定义 realm 安全数据 -->

    <bean id="securityRealm" class="com.vulnverify.web.security.SecurityRealm"></bean>

   

    <!-- Shiro生命周期处理器 -->

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

 

</beans>

2.4 自定义realm 安全数据库

package com.vulnverify.web.security;

import java.util.List;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.SimpleAuthenticationInfo;

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.stereotype.Component;

import com.vulnverify.web.model.TSysRights;

import com.vulnverify.web.model.TSysRole;

import com.vulnverify.web.model.TUser;

import com.vulnverify.web.service.SysRightsService;

import com.vulnverify.web.service.SysRoleService;

import com.vulnverify.web.service.UserService;

/**

 * 用户身份验证,授权 Realm 组件

 *

 * @author linan

 **/

@Component(value = "securityRealm")

public class SecurityRealm extends AuthorizingRealm {

    @Resource

    private UserService sysUserService;

   

    @Resource

    private SysRoleService sysRoleService;

   

    @Resource

    private SysRightsService sysRightsService;

   

    /**

     * 权限检查

     */

    @Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        final TUser user = (TUser)SecurityUtils.getSubject().getSession(false).getAttribute("userInfo");

        String roleType = user.getUserType();//用户角色

        //角色信息

        final TSysRole roleInfos = sysRoleService.selectRoleByRoleType(Integer.parseInt(roleType));

        if(null!=roleInfos){

            // 添加角色

            authorizationInfo.addRole(roleInfos.getRoleName());

            //根据角色id查询角色权限

            final List<TSysRights> sysRightsList = sysRightsService.selectSysRightsByRoleId(roleInfos.getRoleId());

            for (TSysRights sysRights : sysRightsList) {

                // 添加权限

                authorizationInfo.addStringPermission(sysRights.getRightCode());

            }

        }

        return authorizationInfo;

    }

    /**

     * 身份验证信息

     */

    @Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

             //身份

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

        //密码

        String password = new String((char[]) token.getCredentials());

        TUser su = new TUser();

        su.setUserAccount(loginName);

        su.setPassword(password);

        // 通过数据库进行验证

        final TUser authentication = sysUserService.authentication(su);

        if (authentication == null) {

            throw new AuthenticationException("用户名或密码错误.");

        }

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

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(loginName, password, getName());

        return authenticationInfo;

    }

}

2.5 登陆controller

package com.vulnverify.web.controller;

import java.util.Date;

import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import javax.validation.Valid;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.session.Session;

import org.apache.shiro.subject.Subject;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import com.vulnverify.core.entity.PageData;

import com.vulnverify.core.entity.PageQuery;

import com.vulnverify.core.entity.SimpleException;

import com.vulnverify.core.orm.mybatis.Page;

import com.vulnverify.core.redis.RedisDb;

import com.vulnverify.core.utils.ApplicationUtils;

import com.vulnverify.core.utils.DateUtil;

import com.vulnverify.web.constant.Constant;

import com.vulnverify.web.model.TUser;

import com.vulnverify.web.model.requestbody.IdReqBody;

import com.vulnverify.web.model.requestbody.LoginReqBody;

import com.vulnverify.web.model.requestbody.UserCreateReqBody;

import com.vulnverify.web.model.requestbody.UserListReqBody;

import com.vulnverify.web.model.requestbody.UserModifyReqBody;

import com.vulnverify.web.model.responsebody.LoginResBody;

import com.vulnverify.web.model.view.UserView;

import com.vulnverify.web.service.UserService;

/**

 * 用户控制类

 * @author linan

 * @date 2018年4月23日 

 *

 */

@Controller

@RequestMapping(value="/user")

public class UserController extends BaseController{

         private static Logger logger=LoggerFactory.getLogger(UserController.class);

         @Resource

         private UserService userService;

          /**验证码验证是否开启的标识*/

    @Value("${verfication.code.check}") 

    private String verficationCodeCheck = "true";

         /**

          * 登陆

          * @param loginBody

          * @param request

          * @param response

          * @return

          * @throws Exception

          */

         @RequestMapping(value = "/login", method = RequestMethod.POST)

         public Object login(@Valid @RequestBody LoginReqBody loginBody,HttpServletRequest request,HttpServletResponse response) throws Exception{

                   Subject subject = SecurityUtils.getSubject();

        Session session = subject.getSession(false);

             try{

           

            //验证码

                       /*String verificationCode = (String)session.getAttribute("verificationCode");

            if("true".equals(verficationCodeCheck)){

                     if(verificationCode == null || !verificationCode.equalsIgnoreCase(loginBody.getVerificationCode())){

                         throw new SimpleException(Constant.EXCEPTION_S0010005,

                                            ApplicationUtils.getMessage(Constant.EXCEPTION_S0010005));

                }

            }*/

           

            if (subject.isAuthenticated()) {

                     throw new SimpleException(Constant.EXCEPTION_S0010006,

                                        ApplicationUtils.getMessage(Constant.EXCEPTION_S0010006));

            }

            

            final TUser authUserInfo = userService.getUserByUserAccout(loginBody.getUserAccount());

            if(authUserInfo != null){

                     if(authUserInfo.getStatus() == Constant.USER_STATE_UNABLE){

                               throw new Exception("用户"+authUserInfo.getUserAccount()+"已被停用");

                     }

            }

            String sha256Hex = ApplicationUtils.sha256Hex(loginBody.getPassword());

            String password = authUserInfo.getPassword();

            if(sha256Hex.equals(password)){

                     System.out.println("------true---------");

            }

//调用shrio的自定义realm的doGetAuthenticationInfo验证身份           

 subject.login(                          new UsernamePasswordToken(

                                                 loginBody.getUserAccount(), ApplicationUtils.sha256Hex(loginBody.getPassword())));

           

            session.setAttribute("userInfo", authUserInfo);

            session.setAttribute("userKey",loginBody.getUserKey());

            logger.info("login userKey is "+session.getId()+":"+loginBody.getUserKey());

           

          /*  String key = "loginUser."+authUserInfo.getUserAccount();

            RedisDb.setString(key, session.getId().toString());

            RedisDb.expireString(key, 1800);*/

           

            LoginResBody lrb = new LoginResBody();

            lrb.setId(authUserInfo.getUserId()+"");

            lrb.setUserAccount(authUserInfo.getUserAccount());

            lrb.setUserName(authUserInfo.getUserName());

           

            return generateResultData(lrb);

             }catch(Exception e){

                       TUser sysUser = userService.getUserByUserAccout(loginBody.getUserAccount());

                       if(sysUser != null){

                                session.setAttribute("failUserInfo", sysUser);

//                             ApplicationUtils.optData2Request(sysUser.getUserName());

                       }

                       throw e;

             }finally{

                       session.removeAttribute("verificationCode");

             }

         }

         }

2.6权限验证:

2.6.1 shiro什么时候会进入doGetAuthorizationInfo(PrincipalCollection principals)

会进入授权方法一共有三种情况!

1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;

2、在方法上加注解的时候

    @RequiresRoles("admin") :角色验证

   @RequiresPermissions(value = PermissionSign.GET_ORGIP_LIST)权限验证

    PermissionSign为常量类。与realm中授权的添加权限对应。

3、[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。

2.7运行流程:

1.登陆调用userController登陆中的 subject.login(new UsernamePasswordToken(                        loginBody.getUserAccount(), ApplicationUtils.sha256Hex(loginBody.getPassword())));方法

2.调用自定义realm中的doGetAuthenticationInfo(AuthenticationToken token)进行身份登陆验证。

 

3.方法中

@RequiresPermissions()权限会去自定义realm的授权接口doGetAuthorizationInfo(PrincipalCollection principals) 去授权,然后判断是否有操作此方法的权限。

 

3.参考文档:

https://www.cnblogs.com/moonlightL/p/8126910.html

https://blog.csdn.net/mine_song/article/details/61616259

http://jinnianshilongnian.iteye.com/blog/2022468

猜你喜欢

转载自www.cnblogs.com/e206842/p/9047263.html