一、权限框架介绍
- 什么是权限管理
权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。
权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
1.1 用户身份认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。
用户名密码身份认证流程:
1.2 授权流程
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
2. 常见权限框架
2.1 Shiro简介
Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
2.2 Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和授权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一起。安全主要包括两个操作“认证”与“验证”(有时候也会叫做权限控制)。“认证”是为用户建立一个其声明的角色的过程,这个角色可以一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中能够执行某个操作。在到达授权判断之前,角色已经在身份认证过程中建立了。
2.3 Shiro和Spring Security比较
(1)Shiro比Spring更容易使用,实现和最重要的理解
(2)Spring Security更加知名的唯一原因是因为品牌名称
(3)“Spring”以简单而闻名,但讽刺的是很多人发现安装Spring Security很难
(4)Spring Security却有更好的社区支持
(5)Apache Shiro在Spring Security处理密码学方面有一个额外的模块
(6)Spring-security 对spring 结合较好,如果项目用的springmvc ,使用起来很方便。但是如果项目中没有用到spring,那就不要考虑它了。
(7)Shiro 功能强大、且 简单、灵活。是Apache 下的项目比较可靠,且不跟任何的框架或者容器绑定,可以独立运行
二、Shiro基础介绍
Apache Shiro简介
什么是Apache Shiro?
Apache Shiro是一个功能强大且灵活的开源安全框架,可以干净地处理身份验证,授权,企业会话管理和加密。
Apache Shiro的首要目标是易于使用和理解。安全有时可能非常复杂,甚至会很痛苦,但不一定如此。框架应尽可能掩盖复杂性,并公开简洁直观的API,以简化开发人员确保其应用程序安全的工作。
您可以使用Apache Shiro进行以下操作:
验证用户身份以验证其身份
对用户执行访问控制,例如:
确定是否为用户分配了特定的安全角色
确定是否允许用户做某事
即使在没有Web或EJB容器的情况下,也可以在任何环境中使用Session API。
在身份验证,访问控制或会话的生存期内对事件做出反应。
汇总1个或更多用户安全数据的数据源,并将其全部显示为单个复合用户“视图”。
启用单点登录(SSO)功能
启用“记住我”服务以进行用户关联,而无需登录
…
等等-所有这些都集成到一个紧密结合的易于使用的API中。
Shiro尝试在所有应用程序环境中实现这些目标-从最简单的命令行应用程序到最大的企业应用程序,而不必强加对其他第三方框架,容器或应用程序服务器的依赖。当然,该项目旨在尽可能地整合到这些环境中,但是它可以在任何环境中直接使用。
Apache Shiro功能
Apache Shiro是具有许多功能的全面的应用程序安全框架。下图显示了Shiro集中精力的地方,本参考手册的组织方式也类似:
Shiro以Shiro开发团队所谓的“应用程序安全性的四个基石”为目标-身份验证,授权,会话管理和密码术:
身份验证:有时称为“登录”,这是证明用户是他们所说的身份的行为。
授权:访问控制的过程,即确定“谁”有权访问“什么”。
会话管理:即使在非Web或EJB应用程序中,也管理用户特定的会话。
密码学:使用密码算法保持数据安全,同时仍然易于使用。
在不同的应用程序环境中,还具有其他功能来支持和加强这些问题,尤其是:
Web支持:Shiro的Web支持API可帮助轻松保护Web应用程序。
缓存:缓存是Apache Shiro API的第一层公民,可确保安全操作保持快速有效。
并发性:Apache Shiro的并发功能支持多线程应用程序。
测试:测试支持可以帮助您编写单元测试和集成测试,并确保您的代码将按预期进行保护。
“运行方式”:一种功能,允许用户采用其他用户的身份(如果允许),有时在管理方案中很有用。
“记住我”:在各个会话中记住用户的身份,因此他们仅在必要时登录。
- Shiro三个核心组件
1.1 Subject
Subject:即“当前操作用发户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
1.2 SecurityManager
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
1.3 Realm
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
-
Shiro相关类介绍
(1)Authentication 认证 ---- 用户登录
(2)Authorization 授权 — 用户具有哪些权限
(3)Cryptography 安全数据加密
(4)Session Management 会话管理
(5)Web Integration web系统集成
(6)Interations 集成其它应用,spring、缓存框架 -
Shiro 特点
(1)易于理解的 Java Security API;
(2)简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
(3)对角色的简单的签权(访问控制),支持细粒度的签权;
(4)支持一级缓存,以提升应用程序的性能;
(5)内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
(6)异构客户端会话访问;
(7)非常简单的加密 API;
(8)不跟任何的框架或者容器捆绑,可以独立运行
Springboot-shiro项目实战:
1:创建一个springboot项目 2导入thymeleaf依赖 导入shiro依赖项目实践
实现拦截器功能 <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
3.在跟目录下创建config下创建
ShoroConfig .java
package com.xuyuan.config;
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 org.springframework.web.bind.annotation.PathVariable;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShoroConfig {
@Bean
// ShiroFilterFactoryBean:3
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(defaultWebSecurityManager);
/*
*anno:无需认证就可以访问
*authc: 必须认证了才能访问
* user:必须拥有记住我 功能才能访问
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
* */
Map<String, String> Filtermap = new LinkedHashMap<>();
Filtermap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(Filtermap);
//设置登录请求
bean.setLoginUrl("/tologin");
return bean;
}
@Bean(name="SecurityManager")
//DefaultwebSecurityManager:2
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userRealm);
return manager;
}
//创建 realm 对象 ,需要自定义类:1
@Bean
public UserRealm userRealm(){return new UserRealm();}
}
UserRealm.java
package com.xuyuan.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
//自定义 UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权了=》doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行授权了=》doGetAuthenticationInfo");
return null;
}
}
controller
package com.xuyuan.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class indexcontroller {
@RequestMapping({"/","/index"})
public String indexC(Model model){
model.addAttribute("msg","helloShiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/updata")
public String updata(){
return "user/updata";
}
@RequestMapping("/tologin")
public String tologin(){
return "login";
}
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<hr>
<a th:href="@{/user/add}">增加</a> | <a th:href="@{/user/updata}">是修改</a>
</body>
</html>
login
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>
<form action="">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text"name="pawweord"></p>
<p>提交:<input type="submit"></p>
</form>
</p>
</body>
</html>
实现用户认证
controllerpackage com.xuyuan.Controller;
import org.apache.catalina.security.SecurityUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class indexcontroller {
@RequestMapping({"/","/index"})
public String indexC(Model model){
model.addAttribute("msg","helloShiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/updata")
public String updata(){
return "user/updata";
}
@RequestMapping("/tologin")
public String tologin(){
return "login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
subject.login(token);//执行登录方法 如果有异常就不行了
return "index";
}catch (UnknownAccountException e){
//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){//密码错误
model.addAttribute("msg","密码错误");
return "login";
}
}
}
UserRealm
package com.xuyuan.config;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
//自定义 UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权了=》doGetAuthorizationInfo");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行授权了=》doGetAuthenticationInfo");
String name="root";
String password="123456";
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
if(!usertoken.getUsername().equals(name)){
return null;
}
//密码认证
return new SimpleAuthenticationInfo("",password,"");
}
}
index
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<hr>
<a th:href="@{/user/add}">增加</a> | <a th:href="@{/user/updata}">是修改</a>
</body>
</html>
login
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text"name="pawweord"></p>
<p>提交:<input type="submit"></p>
</form>
</body>
</html>