Shiro's identity authentication, integration with spring (entry level)

content
1. Shro concept
2. Shiro's simple authentication implementation
3. Shiro and spring's implementation of identity authentication

  Foreword:

  Shiro can easily develop a good enough application, which can be used not only in the JavaSE environment, but also in the JavaEE environment. Shiro can help us with: authentication, authorization, encryption, session management, integration with the web, caching, etc. Its power is obvious to all, but if we want to enter the world of Shiro, we must first get started. This is an entry-level Shiro tutorial blog.

 

  1. The concept of Shiro

  As the saying goes, "If a person can only write code, then he will be a code farmer all his life", so when we learn a new skill, we don't talk about code first, and after clarifying its concept, we start coding. Remember a word, code is just an appendage of thought.

  Although we only explain identity authentication here, we will introduce its concepts and functions:

  

  1. Basic functions

  

  These are all the basic functions of Shiro. Let's summarize its functions -

  Authentication : Authentication/login, verifying whether the user has the corresponding identity;

 

  Authorization : Authorization, that is, permission verification, verifying whether an authenticated user has a certain permission; that is, judging whether the user can do things, such as: verifying whether a user has a certain role. Or fine-grained to verify whether a user has a certain permission to a certain resource;

 

  Session Manager : Session management, that is, after a user logs in, it is a session, and before exiting, all its information is in the session; the session can be in a normal JavaSE environment or a Web environment;

 

  Cryptography : Encryption to protect the security of data, such as passwords are encrypted and stored in the database instead of plaintext;

 

  Web Support : Web support, which can be easily integrated into the Web environment;

 

  Caching : Caching, for example, after a user logs in, their user information and roles/permissions do not need to be checked every time, which can improve efficiency;

 

  Concurrency : shiro supports concurrent verification of multi-threaded applications, that is, if you start another thread in one thread, permissions can be automatically propagated to the past;

 

  Testing : Provide testing support;

 

  Run As : allows one user to pretend to be another user (if they allow it) to access;

 

  Remember Me : Remember me, this is a very common function, that is, after logging in once, you don't need to log in the next time you come back.

  

  2. Shiro's architecture explained

  Here is a classic Shiro architecture diagram:

  

  This picture is the official Shiro architecture diagram, which can be said to be authoritative. If you thoroughly understand this picture, even if you already know Shiro well, unfortunately I still don’t understand——

 

  Subject : The subject, you can see that the subject can be any "user" who can interact with the application;

 

  SecurityManager : Equivalent to DispatcherServlet in SpringMVC or FilterDispatcher in Struts2; it is the heart of Shiro; all specific interactions are controlled by SecurityManager; it manages all Subjects, and is responsible for authentication and authorization, as well as session and cache management.

 

  Authenticator : Authenticator, responsible for subject authentication, this is an extension point, if the user thinks that Shiro is not good by default, you can customize the implementation; it requires an Authentication Strategy, that is, under what circumstances the user authentication has passed;

 

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

 

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

 

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

 

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

 

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

 

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

 

  3、Shiro的工作流程  

  

  在这个流程里面,我们可以看到API 核心就是 Subject,其中各个部分的解释与作用——

  Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

 

  SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

 

  Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

 

  如果我们写一个最简单的Shiro应用,那么大概流程应该是——

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;

  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

 

  二、Shiro的简单身份认证实现

  我们要做最简单的Shiro身份认证流程,以便于认识Shiro——

  加入依赖:

  

<properties>
        <shiro.version>1.4.0</shiro.version>
</properties>

<dependencyManagement>
     <!-- 对shiro的依赖 -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>
</dependencies>

 

  大概流程为:

  1.   在Realm中设置数据源。
  2.   通过工厂加载配置,通过工厂获取安全管理器,通过工厂获取Subject对象。
  3.   获取验证对象,进行验证。

  上代码:

  ①设置Realm域,Shiro要从Realm域中获取安全、正确的数据。该类必须继承org.apache.shiro.realm.AuthorizingRealm

  并重写doGetAuthorizationInfodoGetAuthenticationInfo方法。doGetAuthorizationInfo是对角色权限的认证,这里暂且不详述;doGetAuthenticationInfo对用户的认证,这里正是我们要详细讲述的地方。代码如下,这里暂且模拟数据,因为我们要做最简单的流程:

 1 package org.ssm.service;
 2 
 3 
 4 import org.apache.shiro.authc.AuthenticationException;
 5 import org.apache.shiro.authc.AuthenticationInfo;
 6 import org.apache.shiro.authc.AuthenticationToken;
 7 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 8 import org.apache.shiro.authz.AuthorizationInfo;
 9 import org.apache.shiro.realm.AuthorizingRealm;
10 import org.apache.shiro.subject.PrincipalCollection;
11 
12 /**
13  * 对shiro进行测试的类
14  * @author zuoyu
15  *
16  */
17 public class CustomRealm extends AuthorizingRealm {
18 
19     @Override
20     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
21         return null;
22     }
23 
24     @Override
25     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
26 //        获取用户的名称信息
27         String userName = (String) authenticationToken.getPrincipal();
28 //        在这里需要从数据库获取信息,我们模拟一下即可
29         
30 //        验证用户名,如果查询不到则返回为空
31         if (!userName.equals("admin")) {
32             return null;
33         }
34         
35 //        根据用户名获取从数据库查询到的密码
36         String dbPassWord = "hello";
37         
38 //        返回认证信息由父类AuthenticatingRealm验证
39         SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
40                 userName,    //输入的正确用户名
41                 dbPassWord,        //对应用户名的正确密码
42                 getName()    //该方法是父类中的方法,不多描述
43         );
44         return simpleAuthenticationInfo;
45     }
46 
47 }

 

  ②接着我们要用借用配置文件使securityManager获取到该Realm域,配置文件如下:

1 [main]
2 #自定义 realm
3 customRealm=org.ssm.service.CustomRealm
4 #将realm设置到securityManager
5 securityManager.realms=$customRealm

  

  ③万事俱备,我们测试一下,测试代码如下:

 1 @Test
 2     public void testShiro() {
 3 //        通过工厂加载配置
 4         Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-shiro.ini");
 5 //        通过工厂获取安全管理器
 6         SecurityManager securityManager = factory.getInstance();
 7 //        注册Subject工具
 8         SecurityUtils.setSecurityManager(securityManager);
 9 //        获取Subject对象
10         Subject subject = SecurityUtils.getSubject();
11 //        获取验证对象
12         AuthenticationToken authenticationToken = new UsernamePasswordToken("admin", "hello");
13 //        验证
14         try {
15             subject.login(authenticationToken);
16             System.out.println("通过..." + "\t" + authenticationToken.getPrincipal());
17         } catch (UnknownAccountException e) {
18             System.out.println("未通过:用户不存在" + "\t" + authenticationToken.getPrincipal());
19             e.printStackTrace();
20         } catch (IncorrectCredentialsException e) {
21             System.out.println("未通过:用户密码不正确" + "\t" + authenticationToken.getPrincipal());
22         } catch (LockedAccountException e) {
23             System.out.println("未通过:用户被锁定" + "\t" + authenticationToken.getPrincipal());
24         }
25         
26     }

  是不是很简单呢!如果我们运用到实际应用中,一般和spring框架结合起来用,有了spring的集成,用起来更方便!

 

  三、Shiro与spring对身份认证的实现

  ①我们需要加入依赖:

  

<properties>
        <shiro.version>1.4.0</shiro.version>
</properties>

<dependencyManagement>
     <!-- 对shiro的依赖 -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>

<!-- spring对shiro的支持 -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
    
</dependencies>

 

  首先,需要配置Shiro的过滤器,对所有访问进行过滤;需要在web.xml中加上如下配置:

<!--配置上shiro过滤器,使其生效-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!--设置url由servlet容器控制filter的生命周期-->
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  

  ③我们需要重新定义Realm域,且安全数据来源于数据库:

  

 1 package org.ssm.shiro.realm;
 2 
 3 import org.apache.shiro.authc.*;
 4 import org.apache.shiro.authz.AuthorizationInfo;
 5 import org.apache.shiro.realm.AuthorizingRealm;
 6 import org.apache.shiro.subject.PrincipalCollection;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.beans.factory.annotation.Qualifier;
 9 import org.ssm.entity.Users;
10 import org.ssm.service.UsersService;
11 
12 
13 /**
14  * 用户认证
15  * @author zuoyu
16  */
17 public class UserRealm extends AuthorizingRealm {
18 
19     /**
20      * 引入users操作的整合操作
21      */
22     @Autowired
23     @Qualifier("usersService")
24     private UsersService usersService;
25 
26     /**
27      *获取授权信息
28      * @param principalCollection
29      * @return
30      */
31     @Override
32     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
33         return null;
34     }
35 
36     /**
37      * 获取获取身份验证相关信息
38      * @param authenticationToken
39      * @return
40      * @throws AuthenticationException
41      */
42     @Override
43     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
44 //        获取用户输入的用户名
45         String userName = (String) authenticationToken.getPrincipal();
46 //        根据用户名查询该数据
47         Users user = usersService.selectByName(userName);
48 
49         if (user == null){
50 //            没有找到账号
51             throw new UnknownAccountException();
52         }
53         SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassWord(),
54 //                此为realm内的方法,用于获取realm的name
55                 getName()
56         );
57         return authenticationInfo;
58     }
59 }

 

  ④需要在spring的applicationContext.xml配置文件加入对Shiro的Bean依赖注入的处理:

<!--shiro的配置-->

    <!--注入自定义的Realm-->
    <bean id="userRealm" class="org.ssm.shiro.realm.UserRealm"/>
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/>
    </bean>


    <!--配置ShiroFilter的属性-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="filterChainDefinitions">
            <value>
                /login = anon
            </value>
        </property>
    </bean>

  

  ⑤写一个springMVC的拦截器,对登陆进行拦截:

 1 package org.ssm.aop;
 2 
 3 import org.apache.shiro.SecurityUtils;
 4 import org.apache.shiro.subject.Subject;
 5 import org.springframework.web.servlet.AsyncHandlerInterceptor;
 6 import org.springframework.web.servlet.HandlerInterceptor;
 7 import org.springframework.web.servlet.ModelAndView;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 /**
12  * 登陆拦截
13  * @author zuoyu
14  */
15 public class LoginInterceptor implements HandlerInterceptor {
16 
17     /**
18      * 登陆拦截
19      * {@link AsyncHandlerInterceptor}.
20      *
21      * @param request  current HTTP request
22      * @param response current HTTP response
23      * @param handler  chosen handler to execute, for type and/or instance evaluation
24      * @return {@code true} if the execution chain should proceed with the
25      * next interceptor or the handler itself. Else, DispatcherServlet assumes
26      * that this interceptor has already dealt with the response itself.
27      * @throws Exception in case of errors
28      */
29     @Override
30     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
31 
32 //        从该回话中获取subject
33         Subject subject = SecurityUtils.getSubject();
34 //        获取用户信息
35         String user = (String) subject.getPrincipal();
36 //        如果用户信息为空,则进行拦截
37         if (user != null){
38             return true;
39         }
40         response.sendRedirect(request.getContextPath() + "/User/login");
41         return false;
42     }
43 
44 
45     @Override
46     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
47 
48     }
49 
50 
51     @Override
52     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
53 
54     }
55 
56 
57 }

  

  ⑥我们需要在springMVC的配置文件中设置一下该拦截器的拦截对象以及哪些不需要拦截,加上如下代码:

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/assets/**"/>
            <mvc:exclude-mapping path="/User/login"/>
            <mvc:exclude-mapping path="/logout"/>
            <bean class="org.ssm.aop.LoginInterceptor"/>
        </mvc:interceptor>
</mvc:interceptors>

  

  ok,这样就完成了基于spring集成的简单Shiro身份认证,前端页面自己写就可以了。

  如果觉得我的博客写的还不错,关注一下哦!我会不定时更新。

  不问前程,但行好事...

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325293041&siteId=291194637