shiro入门与springboot整合shiro

简介: Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,是目前用得比较多的登陆框架。

Shiro原理

Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,是目前用得比较多的登陆框架。

基本框架


  1. Subject:主体,代表了当前“用户”。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等。所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager。我们可以把 Subject 认为是一个门面,SecurityManager 才是实际的执行者。
  2. SecurityManager:安全管理器。即所有与安全有关的操作都会与 SecurityManager 交互,且它管理着所有 Subject。可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,我们可以把它看成 DispatcherServlet 前端控制器。
  3. Realm:域。Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法,也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作。我们可以把 Realm 看成 DataSource,即安全数据源。

运行原理

  1. Subject:主体,可以看到主体可以是任何与应用交互的“用户”。
  2. SecurityManager:相当于 SpringMVC 中的 DispatcherServlet。它是 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、应用程序构建了一个终端用户认证信息的AuthenticationToken 实例后,调用Subject.login方法。

2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。

3、SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token). ModularRealmAuthenticator在认证过程中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。

4、如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每一个Realm的结果作出响应。

注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。

5、判断每一个Realm是否支持提交的token,如果支持,Realm将调用getAuthenticationInfo(token); getAuthenticationInfo 方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。

   @PostMapping("/login")

   publicStringlogin(Stringusername, Stringpassword, Modelmodel){

       Subjectsubject=SecurityUtils.getSubject();

       UsernamePasswordTokentoken=newUsernamePasswordToken(username,password);

       //捕获异常

       try {

           subject.login(token);//将token交给shiro管理,进入自定义的realm中的验证方法

           Accountaccount= (Account) subject.getPrincipal();//登录成功之后,取出当前用户

           subject.getSession().setAttribute("account", account);//将用户存入session

           return"redirect:index";

       } catch (UnknownAccountExceptione) {//账户的异常

           e.printStackTrace();

           model.addAttribute("msg", "用户名错误");

           return"loginPage";

       }catch (IncorrectCredentialsExceptione){//密码的异常

           e.printStackTrace();

           model.addAttribute("msg", "密码错误");

           return"loginPage";

       }

   }

Shiro 核心组件

author:作者

authorization:权限授予(授权,认可;批准,委任)

authentic:真正的,真实的;可信的

authentication:身份验证(证明;鉴定;证实)

用户、角色、权限

会给角色赋予权限,给用户赋予角色

UsernamePasswordToken

用来封装用户登录信息,使用用户的信息来创建令牌 Token

创建UsernamePasswordToken对象,参数为username和password

UsernamePasswordTokentoken=newUsernamePasswordToken(username,password);

Subject

subject:主题;起因;科目;主词;(绘画、摄影等的)题材;实验对象;主语;国民;主旋律;主体;中心实体

Shiro中认证授权组件Subject,为我们提供了当前用户、角色和授权的相关信息,可以进行登录,退出,权限验证,获取用户信息,session。

获取subject

Subjectsubject=SecurityUtils.getSubject();

0. 登录

所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager。我们可以把 Subject 认为是一个门面,SecurityManager 才是实际的执行者

subject.login(token);//subject会委托securityManager实例调用securityManager.login(token)方法,最终传到realm方法中

1. 获得Session对象

Sessionsession=subject.getSession();

session.setAttribute( "key", "aValue" );

这里的Session并不是HttpSession,而是shiro为我们提供的,它的操作与HttpSession一样,他们最大的区别就是shiro session不需要依赖http服务器。

2. 得到当前登录的用户名

StringcurrentUser=subject.getPrincipal().toString();

System.out.println("当前登录的用户是:"+currentUser);

3. 校验当前用户的权限

//判断用户是否是拥有某种角色

booleanisRole=subject.hasRole( "admin" );

//是否拥有某种功能

booleanisPer=subject.isPermitted("xiaoming:run");

4. 退出登录

//退出登录

subject.logout();

SecurityManager

外观模式(facade):为子系统中一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

SecurityManager是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例,如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略

Realm

realm:领域,范围;王国

Realm提供待验证数据的比对值,即安全数据源,可以理解为数据的源头,可以是数据库,文件等。

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

Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。从这个意义上讲,Realm 实质上是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro 。当配置 Shiro时,你必须至少指定一个 Realm ,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。

Spring Boot 整合 Shiro

1. 导入依赖

   <dependency>

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

       <artifactId>shiro-spring</artifactId>

       <version>1.5.3</version>

   </dependency>

2. 编写Realm

realm域中存放着真实数据

packagecn.upeveryday.realm;

publicclassAccountRealmextendsAuthorizingRealm {

   @Autowired

   privateAccountServiceaccountService;

   /**

    * 获取授权信息:获取当前用户的角色和权限

    * @param principalCollection

    * @return

    */

   @Override

   protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipalCollection) {

       //获取当前用户主体

       Subjectsubject=SecurityUtils.getSubject();

       Accountaccount= (Account) subject.getPrincipal();

       //获取当前用户角色

       HashSet<String>roles=newHashSet();

       roles.add(account.getRole());

       //SimpleAuthorizationInfo是AuthorizationInfo的子类

       SimpleAuthorizationInfoinfo=newSimpleAuthorizationInfo(roles);

       //获取当前用户权限

       info.addStringPermission(account.getPerms());

       //返回当前用户的授权信息(包括角色与权限)

       returninfo;

   }

   /**

    * 获取身份验证信息:将前端传来的用户信息与安全数据进行比较

    * @param authenticationToken:就是controller中传来的token,subject.login(token);

    * @return

    * @throws AuthenticationException

    */

   @Override

   protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokenauthenticationToken) throwsAuthenticationException {

       UsernamePasswordTokentoken= (UsernamePasswordToken) authenticationToken;

       //用户名验证手写,拿到account后,密码验证就不用手写了,直接:new SimpleAuthenticationInfo(对象本身,对象密码,对象名字),会自动将对象密码与token中的密码进行验证

       Accountaccount=accountService.findByUsername(token.getUsername());

       if (account!=null){

           returnnewSimpleAuthenticationInfo(account,account.getPassword(),getName());

       }

       returnnull;

   }

}

3. 编写ShiroConfig

  1. 将自定义的realm对象装配进IOC容器
  2. 将安全管理器对象DefaultWebSecurityManager装配进IOC容器,并将realm插入管理器中
  3. 将过滤器工厂对象ShiroFilterFactoryBean装配进IOC容器,并将DefaultWebSecurityManager装配进工厂
    在过滤器工厂中配置并创建过滤器

认证过滤器

anon:无需认证。

authc:必须认证(必须登录状态)

authcBasic:需要通过 HTTPBasic 认证。

user:不一定通过认证,只要曾经被 Shiro 记录即可,比如:记住我的操作

授权过滤器

perms:必须拥有某个权限才能访问。

role:必须拥有某个角色才能访问。

port:请求的端口必须是指定值才可以。

rest:请求必须基于 RESTful,POST、PUT、GET、DELETE。

ssl:必须是安全的 URL 请求,协议 HTTPS。

package cn.upeveryday.config;


import cn.upeveryday.realm.AccountRealm;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

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

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;


import java.util.HashMap;


@Configuration

public class ShiroConfig {


   @Bean

   public AccountRealm accountRealm(){

       return new AccountRealm();

   }


   //自定义的 Realm 需要插入 DefaultWebSecurityManager 进行管理才能生效

   @Bean

   public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

       DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

       manager.setRealm(accountRealm);

       return manager;

   }


   @Bean

   public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){

       ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

       factoryBean.setSecurityManager(defaultWebSecurityManager);

       //配置过滤器

       HashMap<String, String> map = new HashMap<>();

       map.put("/main","authc");//请求/main时,必须是登录状态

       map.put("/manage", "perms[manage]");//必须有manage权限才可以访问

       map.put("/administrator", "roles[administrator]");//必须是administrator才可以访问

       //创建过滤器链

       factoryBean.setFilterChainDefinitionMap(map);

       //设置登录界面:当未登录状态访问其他页面时,会自动跳转到登录界面

       factoryBean.setLoginUrl("/login");

       //设置未授权401显示界面,提升界面友好度

       factoryBean.setUnauthorizedUrl("/unauthorized");

       return factoryBean;

   }

}

4. Shiro 整合 Thymeleaf

1、pom.xml 引入依赖

<dependency>

   <groupId>com.github.theborakompanioni</groupId>

   <artifactId>thymeleaf-extras-shiro</artifactId>

   <version>2.0.0</version>

</dependency>

2、配置类添加 ShiroDialect(配置方言)

   //shiro整合thymeleaf

   @Bean

   public ShiroDialect shiroDialect(){

       return new ShiroDialect();

   }

3、在HTML中使用

  • 引入命名空间

xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"

  • 当前用户符合权限或角色才显示当前标签,否则不显示

   <div shiro:hasPermission="manage">

       <a href="manage">manage</a> <br/>

   </div>

   

   <div shiro:hasRole="administrator">

       <a href="/administrator">administrator</a>

   </div>

4、实例

<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">

<head>

   <meta charset="UTF-8">

   <title>Title</title>

   <link rel="shortcut icon" href="#"/>

</head>

<body>

   <h1>index</h1>

   <div th:if="${session.account != null}">

       <span th:text="${session.account.username}+'欢迎回来!'"></span><a href="/logout">退出</a>

   </div>

   <a href="/main">main</a> <br/>

   

   <div shiro:hasPermission="manage">

       <a href="manage">manage</a> <br/>

   </div>

   

   <div shiro:hasRole="administrator">

       <a href="/administrator">administrator</a>

   </div>

</body>

</html>

controller

package cn.upeveryday.controller;


@Controller

public class AccountController {


   @GetMapping("/{url}")

   public String redirect(@PathParam("url")String url){

       return url;

   }


   @PostMapping("/login")

   public String login(String username, String password, Model model){

       Subject subject= SecurityUtils.getSubject();

       UsernamePasswordToken token=new UsernamePasswordToken(username,password);

       //捕获异常

       try {

           subject.login(token);//subject会委托应用程序设置的securityManager实例调用securityManager.login(token)方法

           Account account= (Account) subject.getPrincipal();//登录成功之后,取出当前用户

           subject.getSession().setAttribute("account", account);//将用户存入session

           return "redirect:index";

       } catch (UnknownAccountException e) {//账户的异常

           e.printStackTrace();

           model.addAttribute("msg", "用户名错误");

           return "loginPage";

       }catch (IncorrectCredentialsException e){//密码的异常

           e.printStackTrace();

           model.addAttribute("msg", "密码错误");

           return "loginPage";

       }

   }


   @GetMapping("/unauthorized")

   @ResponseBody

   public String unauthorized(){

       return "无权限访问";

   }


   @GetMapping("/logout")

   public String logout(){

       //销毁session

       Subject subject = SecurityUtils.getSubject();

       subject.logout();

       return "redirect:/loginPage";

   }

}

账户实体

package cn.upeveryday.entity;


@Data

public class Account {

   private Integer id;

   private String username;

   private String password;

   private String perms;

   private String role;

}


目录
相关文章
|
15天前
|
Java 应用服务中间件 数据库连接
SpringBoot入门(2) - SpringBoot HelloWorld
SpringBoot入门(2) - SpringBoot HelloWorld
33 2
SpringBoot入门(2) - SpringBoot HelloWorld
|
26天前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
43 4
SpringBoot入门(4) - 添加内存数据库H2
|
16天前
|
Java 中间件
SpringBoot入门(6)- 添加Logback日志
SpringBoot入门(6)- 添加Logback日志
61 5
|
15天前
|
前端开发 Java 数据库
SpringBoot入门(3) - 对Hello world进行MVC分层
SpringBoot入门(3) - 对Hello world进行MVC分层
31 4
|
16天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
27 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
26天前
|
Java 应用服务中间件 数据库连接
SpringBoot入门(2) - SpringBoot HelloWorld
SpringBoot入门(2) - SpringBoot HelloWorld
19 2
SpringBoot入门(2) - SpringBoot HelloWorld
|
29天前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
31 2
SpringBoot入门(4) - 添加内存数据库H2
|
29天前
|
前端开发 Java 数据库
SpringBoot入门(3) - 对Hello world进行MVC分层
SpringBoot入门(3) - 对Hello world进行MVC分层
35 1
SpringBoot入门(3) - 对Hello world进行MVC分层
|
29天前
|
Java 应用服务中间件 数据库连接
SpringBoot入门(2) - SpringBoot HelloWorld
SpringBoot入门(2) - SpringBoot HelloWorld
20 1
 SpringBoot入门(2) - SpringBoot HelloWorld
|
21天前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
60 13
下一篇
无影云桌面