当前位置: 首页 >Java技术 > SpringBoot集成Apache Shiro

SpringBoot集成Apache Shiro

笔者因为项目转型的原因,对Apache Shiro安全框架做了一点研究工作,故想写点东西以便将来查阅。之所以选择Shiro也是看了很多人的推荐,号称功能丰富强大,而且易于使用。实践下来的确如大多数人所说简约优美,小巧精悍。

介绍demo项目前,简单说明一下Shiro框架的特性。

1.  Apache Shiro Features

 

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记

 

从上图可以看出Shiro具备应用程序安全框架的四大基石”:身份验证、授权、会话管理和密码。

Authentication有时被称为‘登录’,这是需要明确用户是谁

Authorization访问控制,即确定‘谁’对‘什么’有访问权限。

Session Management管理特定用户的会话,即使在非web或EJB应用程序中也是如此。

Cryptography使用加密算法保持数据安全,但易于使用。

 

在不同的应用程序环境中,还有更多的特性来支持和增强这些关注点,特别是:

Web SupportShiro的Web支持API帮助轻松地保护web应用程序。

Caching缓存是ApacheShiro的API中的第一等公民,以确保安全操作同时保持快速和高效。

ConcurrencyApacheShiro支持具有并发特性的多线程应用程序。

Testing提供测试支持,以帮助编写单元和集成测试,并确保代码如预期的安全。

Run as允许用户假定另一个用户的身份(如果允许的话)的特性,有时在管理场景中很有用。

Remember Me记住用户在会话中的身份,这样他们就只需要在强制的情况下输入口令登录。

 

2. High-Level Overview

Shiro的体系结构有三个主要概念:Subject、SecurityManager和Realms。下图展现了它的运行原理,

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记

主题:主题本质上是当前正在执行的用户。虽然“用户”这个词通常意味着一个人,一个主题可以是一个人,但它也可以代表一个第三方服务、守护进程帐户、cron作业或任何类似的东西-基本上是任何当前与软件交互的东西。Subject实例都绑定到(并且需要)一个SecurityManager。当与主题交互时,这些交互转化为与SecurityManager的特定主题交互。

 

SecurityManagerSecurityManager是Shiro体系结构的核心,它将其内部安全组件协调在一起形成一个对象图。然而,一旦为应用程序配置了SecurityManager及其内部对象图,它通常会被单独使用,应用程序开发人员将几乎所有的时间都花在Subject API上。当与一个主题交互时,实际上是幕后的SecurityManager为任何主题安全操作做了所有繁重的工作。

 

领域:领域充当Shiro和应用程序安全数据之间的“桥梁”或“连接器”。当涉及到实际与用户帐户等安全相关的数据交互以执行身份验证(登录)和授权(访问控制)时,Shiro从一个或多个为应用程序配置的领域中查找数据。从这个意义上说,领域本质上是一个特定于安全的DAO:它封装数据源的连接细节,并根据需要将相关数据提供给Shiro。配置Shiro时,必须指定至少一个用于身份验证和/或授权的域。SecurityManager可以配置多个Realm,但至少需要一个。Shiro提供了开箱即用的领域,以连接到许多安全数据源(也称为目录),如LDAP、关系数据库(JDBC)、INI和属性文件等文本配置源。

 

3. Detailed Architecture

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记

 

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


项目中常用的解释一下, 

/test/**=anon,  所有url 可以匿名访问

/test/**=authc,  url需要认证才能访问

/test/**=perms[user:add],  url需要认证用户拥有 user:add 权限才能访问

/test/**=roles[admin],  url需要认证用户拥有 admin 角色才能访问

/test/**=user,  url 需要认证或通过记住我认证才能访问

 

5. DEMO

开发工具为Eclipse+Maven,新建Springboot项目,版本号为1.5.14

模板引擎使用Thymeleaf

为了突出shiro,数据访问层略过,服务层模拟查询数据,Realm里面硬编码权限和角色信息简化代码。

整个系统的核心在于两个class(MyShiroRealm + ShiroConfiguration), 项目结构如下,

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记

5.1 在pom.xml里面添加好shiro-core, shiro-spring

<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version></dependency><!-- shiro权限控制框架 --><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version></dependency>

 

5.2 显示层

模板引擎Thymeleaf,故application配置文件如下:

1 spring.thymeleaf.cache=true2 spring.thymeleaf.prefix=classpath:/templates/3 spring.thymeleaf.suffix=.html4 spring.thymeleaf.mode=HTML55 spring.thymeleaf.encoding=UTF-86 spring.thymeleaf.content-type=text/html

 

5.3 显示层静态页面如下:

403.html

add.html

delete.html

details.html

edit.html

index.html

login.html

logout.html

 

5.4 几乎所有静态页面就是一个空壳,大体如add.html

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"></meta> 5 <title>Add Page</title> 6 </head> 7 <body> 8 <h1>Add Page</h1> 9 </body>10 </html>
add.html
SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"></meta> 5 <title>Login Page</title> 6 <style type="text/css"> 7 table { 8 width: 360px; 9 min-height: 25px;10 line-height: 25px;11 text-align: center;12 border-color: #b6ff00;13 border-collapse: collapse;14 15 }16 </style>17 </head>18 <body>19 <div>20 <form action="/home/check" method="post">21 <table border="1">22 <tr>23 <th>User Name</th>24 <th><input type="text" name="name" /></th>25 </tr>26 <tr>27 <td>Password</td>28 <td><input type="password" name="password" /></td>29 </tr>30 <tr>31 <td><input type="submit" value="Submit" /></td>32 <td></td>33 </tr>34 </table>35 </form>36 </div>37 </body>38 </html>
login.html
SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"></meta> 5 <title>Index Page</title> 6 <style type="text/css"> 7 p { 8 font-family: Times, TimesNR, 'New Century Schoolbook', Georgia, 9 'New York', serif;10 font-size: 20px;11 }12 </style>13 14 </head>15 <body>16 <h1>This is index page.</h1>17 <p>18 Customer Name: <span th:text="${name}"></span> --- Role: <span19 th:text="${role}"></span>20 </p>21 <table border="1">22 <tr>23 <th>角色</th>24 <th>权限</th>25 </tr>26 <tr>27 <td>admin</td>28 <td>增加,删除,编辑,查看</td>29 </tr>30 <tr>31 <td>operator</td>32 <td>编辑,查看</td>33 </tr>34 <tr>35 <td>viewer</td>36 <td>查看</td>37 </tr>38 </table>39 <ul>40 <li><a th:href="@{/customer/index}">Index</a></li>41 <li><a th:href="@{/customer/details}">Details</a></li>42 <li><a th:href="@{/customer/add}">Add</a></li>43 <li><a th:href="@{/customer/edit}">Edit</a></li>44 <li><a th:href="@{/customer/delete}">Delete</a></li>45 </ul>46 </body>47 </html>
index.html

5.5 Model

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
public class Customer implements Serializable {private static final long serialVersionUID = 7429292944316962328L;private String name;private String password;private String role;public String getName() {retu name;}public void setName(String name) {this.name = name;}public String getPassword() {retu password;}public void setPassword(String password) {this.password = password;}public String getRole() {retu role;}public void setRole(String role) {this.role = role;}public Customer(String name, String password, String role) {super();this.name = name;this.password = password;this.role = role;}@Overridepublic String toString() {retu "Name : --- " + name + ", Password : --- " + password + ", Role : *** " + role;}}
Customer.java

5.6 Service

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 @Service 2 public class CustomerService { 3 public Customer findByName(String name) { 4 // 模拟查询数据库 5 // tom is admin, alice is operator, lucy is viewer 6 if (name.equals("alice")) { 7 retu new Customer(name, "123", "operator"); 8 } 9 retu null;10 }11 }
CustomerService.java

5.7 Controller

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
@Controller@RequestMapping("customer")public class CustomerController {@RequestMapping("/index")@RequiresPermissions("customer:index")//权限管理; public String index(Model model) {Subject subject = SecurityUtils.getSubject();Customer customer = (Customer)subject.getPrincipal();if(customer != null) {model.addAttribute("name", customer.getName());model.addAttribute("role", customer.getRole());}retu "index";}@RequestMapping("/details")@RequiresPermissions("customer:details")//权限管理; public String details() {retu "details";}@RequestMapping("/add")@RequiresRoles("admin")public String add() {retu "add";}@RequestMapping("/edit")@RequiresPermissions("customer:edit")//权限管理; public String edit() {retu "edit";}@RequestMapping("/delete")@RequiresPermissions("customer:delete")//权限管理;public String delete() {retu "delete";}}
CustomerController.java
SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
@Controller@RequestMapping("home")public class HomeController {@RequestMapping("/login")public String login() {retu "login";}@RequestMapping("/check")public String check(HttpServletRequest request) throws Exception {System.out.println("HomeController.check()");String name = request.getParameter("name");String password = request.getParameter("password");UseamePasswordToken token = new UseamePasswordToken(name, password);Subject subject = SecurityUtils.getSubject();try {subject.login(token);} catch (Exception ex) {System.out.println(ex.getMessage());System.out.println(ex.getStackTrace());retu "login";}retu "redirect:/customer/index";}}
HomeController.java

5.8 最重要的两个类如下:

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 package com.example.demo.config; 2  3 import java.util.LinkedHashMap; 4 import java.util.Map; 5  6 import org.apache.shiro.mgt.SecurityManager; 7 import org.apache.shiro.spring.LifecycleBeanPostProcessor; 8 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;10 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;11 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;12 import org.springframework.context.annotation.Bean;13 import org.springframework.context.annotation.Configuration;14 15 @Configuration16 public class ShiroConfiguration {17 18 @Bean19 public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {20 System.out.println("ShiroConfiguration.shirFilter()");21 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();22 shiroFilterFactoryBean.setSecurityManager(securityManager);23 // 过滤器.24 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();25 // 配置不会被拦截的链接 顺序判断26 filterChainDefinitionMap.put("/static/**", "anon");27 filterChainDefinitionMap.put("/home/**", "anon");28 filterChainDefinitionMap.put("/test/**", "anon");29 filterChainDefinitionMap.put("/customer/**", "authc");30 shiroFilterFactoryBean.setLoginUrl("/home/login");31 // 登录成功后要跳转的链接32 shiroFilterFactoryBean.setSuccessUrl("/customer/index");33 34 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);35 retu shiroFilterFactoryBean;36 }37 38 @Bean39 public MyShiroRealm myShiroRealm() {40 MyShiroRealm myShiroRealm = new MyShiroRealm();41 retu myShiroRealm;42 }43 44 @Bean45 public SecurityManager securityManager() {46 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();47 securityManager.setRealm(myShiroRealm());48 retu securityManager;49 }50 51 // 开启Shiro AOP注解支持.52 @Bean53 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {54 System.out.println("OPNE AOP......");55 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();56 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);57 retu authorizationAttributeSourceAdvisor;58 }59 60 @Bean61 public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {62 retu new DefaultAdvisorAutoProxyCreator();63 }64 65 // 管理shiro生命周期66 @Bean67 public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {68 retu new LifecycleBeanPostProcessor();69 }70 }
ShiroConfiguration.java

因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.

Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。

该方法主要执行以下操作:

 

1)         根据口令信息检查标识主体(帐户标识信息)

2)         在数据源中查找相应的帐户信息

3)         确保令牌提供的凭据与存储在数据存储中的凭据匹配

4)         如果凭证匹配,则返回一个AuthenticationInfo实例,该实例将帐户数据封装为Shiro理解的格式

5)         如果凭证不匹配,则引发身份验证异常

 

在应用程序中需要自定义一个Realm类,继承AuthorizingRealm抽象类,覆盖doGetAuthenticationInfo(),重写获取用户信息的方法。

 

shiro的权限授权是通过继承AuthorizingRealm抽象类,覆盖doGetAuthorizationInfo()。当访问到页面的时候,URL配置了相应的权限或者shiro标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。

在这个方法中主要是使用类:SimpleAuthorizationInfo进行角色的添加和权限的添加。SecurityManager将权限或角色检查的任务委托给Authorizer,默认为ModularRealmAuthorizer。

应用程序则可以通过角色或者权限进行访问控制。

SpringBoot集成Apache Shiro _ JavaClub全栈架构师技术笔记
 1 package com.example.demo.config; 2  3 import java.util.HashSet; 4 import java.util.Set; 5  6 import org.apache.shiro.authc.AuthenticationException; 7 import org.apache.shiro.authc.AuthenticationInfo; 8 import org.apache.shiro.authc.AuthenticationToken; 9 import org.apache.shiro.authc.SimpleAuthenticationInfo;10 import org.apache.shiro.authz.AuthorizationInfo;11 import org.apache.shiro.authz.SimpleAuthorizationInfo;12 import org.apache.shiro.realm.AuthorizingRealm;13 import org.apache.shiro.subject.PrincipalCollection;14 import org.springframework.beans.factory.annotation.Autowired;15 16 import com.example.demo.model.Customer;17 import com.example.demo.service.CustomerService;18 19 public class MyShiroRealm extends AuthorizingRealm {20 21 @Autowired22 private CustomerService customerService;23 24 @Override25 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {26 System.out.println("MyShiroRealm.doGetAuthenticationInfo()");27 28 // 获取用户的输入的账号.29 String name = (String) token.getPrincipal();30 System.out.println(token.getCredentials());31 Customer c = customerService.findByName(name);32 System.out.println("Customer info : " + c);33 if (c == null) {34 retu null;35 }36 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(c, // 用户名37 c.getPassword(), // 密码38 getName() // realm name39 );40 41 retu authenticationInfo;42 }43 44 @Override45 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {46 47 System.out.println("权限管理-->MyShiroRealm.doGetAuthorizationInfo()");48 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();49 Customer customer = (Customer) principals.getPrimaryPrincipal();50 System.out.println("Customer is : " + customer);51 // 权限单个添加;52 // 添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色53 // 模拟查询数据库,得到用户角色为admin或者operator或者viewer54 // admin有所有权限,operator有查看和编辑权限,没有添加和删除权限55 // viewer只有查看权限56 authorizationInfo.addRole("operator");57 // 添加权限58 Set<String> permissionSet = new HashSet<String>();59 permissionSet.add("customer:details");60 permissionSet.add("customer:index");61 permissionSet.add("customer:edit");62 //permissionSet.add("customer:add");63 //permissionSet.add("customer:delete");64 65 authorizationInfo.setStringPermissions(permissionSet);66 retu authorizationInfo;67 }68 }
MyShiroRealm

 

6. 参考资料

http://shiro.apache.org/introduction.html

 

作者:沐璟
来源链接:https://www.cnblogs.com/sankt/p/9278886.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/java/112565.html

标签:Shiro
分享给朋友:

“SpringBoot集成Apache Shiro” 的相关文章