Springboot整合Shiro安全框架
最近在学习Springboot,在这个过程中遇到了很多之前都没有技术知识,学习了一阵子,稍微总结一些。
---- Shiro框架
shiro框架,是一个相对比较简便的安全框架,它可以干净利落地处理身份验证、授权、企业会话管理和加密。
在此引入网上众多资料,归总shiro大致框架如下:

- Subject: 主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
- Shiro SecurityManager: 管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞.
- Realm: 用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)
在主要开发上,我将Shiro的实际应用归结成两大块,其一是关于shiro的一些配置信息,这里称为ShiroConfig文件,由于Apache Shiro 核心通过 Filter 来实现,既然是使用 Filter 一般也就能猜到,是通过 URL 规则来进行过滤和权限校验,所以我们需要定义一系列关于 URL 的规则和访问权限,以及一些Shiro自身具备的一些Bean工具。其二是关于我们自己需要定义的Realm类,这里称为ShiroRealm文件,继承AuthorizingRealm 抽象类,重载 doGetAuthenticationInfo(),重写获取用户信息的方法,重写doGetAuthorizationInfo(),进行角色权限的配置。
---?ShiroConfig
package springbootshiro2.demo.configurer; import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap;
import java.util.Map; /**
* 〈一句话功能简述〉<br>
* 〈〉
*
* @author X450J
* @create 2019/5/30
* @since 1.0.0
*/
@Configuration
public class ShiroConf { //注入shiro过滤器
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFiltr(WebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setSuccessUrl("/index");// 登录成功后要跳转的链接
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");//设置无权限跳转页面
Map<String,String> chains = new LinkedHashMap<>();
chains.put("/logout","logout");
chains.put("/login", "anon");//anon表示可以匿名访问
chains.put("/login/qq", "anon");//anon表示可以匿名访问
chains.put("/authorize/qq", "anon");//anon表示可以匿名访问
//对PermissionAction.class 中的url进行权限控制
chains.put("/user", "roles[user]");//需要user角色才可以访问
chains.put("/user/per", "perms[user:query]");//需要user角色才可以访问
chains.put("/admin", "roles[admin]");//需要admin角色才可以访问
//chains.put("/**", "authc");//表示需要认证,才能访问
chains.put("/**", "user");//表示需要认证或记a住我都能访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(chains);
return shiroFilterFactoryBean;
}
//安全管理器
@Bean
public WebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm
securityManager.setRealm(shiroRealm());
securityManager.setCacheManager(ehCacheManager());
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
} //会话管理器
@Bean
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(true);
sessionManager.setGlobalSessionTimeout(1 * 60 * 60 * 1000);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionIdCookie(rememberMeCookie());
return sessionManager;
} //Realm,里面需要自己实现认证和授权业务
@Bean
public ShiroRealm shiroRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return shiroRealm;
} //缓存管理
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
} //密码管理
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(2); //加密两次
credentialsMatcher.setStoredCredentialsHexEncoded(true); //启用十六进制
return credentialsMatcher;
}
//cookie管理
@Bean
public SimpleCookie rememberMeCookie() {
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);
cookie.setMaxAge(1 * 60 * 60);
return cookie;
} //管理shiro的生命周期
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} //记住我
@Bean
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//不是所有的base64编码都可以用,长度过大过小都不行
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
} //开启shiro注解权限控制
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourcteAdvisor;
} }
- LifecycleBeanPostProcessor:它管理Shiro的生命周期。
- ShiroFilterFactoryBean:采用了工厂设计模式,负责引入SecurityManager、登录的页面url设置、登录后要跳转的url设置、未授权的页面的url设置、拦截器链的设置。
- SecurityManager:Shiro的核心,认证器、缓存器、Realm数据源都在这里配置注入
- HashedCredentialsMatcher:Shiro自带密码器,这里用了MD5加密算法,并且还将其注入到自定义的ShiroRealm之中。
- CacheManager: 缓存管理器,这里引入了Ehcache框架来实现缓存,接着再一起注入安全管理器(SecurityManager)
- AuthorizationAttributeSourceAdvisor: 配置生效后,可以开启shiro的注解权限控制
- CookieRememberMeManager: 顾名思义,负责实现RememberMe功能的,使得浏览器请求页面时可以通过cookies判断用户而无需登录
- SessionManager: 关于session会话的一些管理
---?ShiroRealm
package springbootshiro2.demo.configurer; import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import springbootshiro2.demo.model.User; /**
* 〈一句话功能简述〉<br>
* 〈自定义Realm〉
*
* @author X450J
* @create 2019/5/30
* @since 1.0.0
*/
public class ShiroRealm extends AuthorizingRealm { @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//给当前角色授权
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); User user = (User) principalCollection.getPrimaryPrincipal();
//在这里 实际开发应该从数据库进行获取
if (user.getUsername().equals("xslde")){
//设置该用户拥有user角色
authorizationInfo.addRole("user");
//设置该用户拥有query权限
authorizationInfo.addStringPermission("user:query");
} if(user.getUsername().equals("admin")){
//admin用户拥有admin、user角色
authorizationInfo.addRole("admin");
authorizationInfo.addRole("user");
//设置该用户拥有query权限
authorizationInfo.addStringPermission("user:query");
}
return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//进行用户信息获取
String username = (String)token.getPrincipal();
//开发中,这里都是去数据库查询
if (!"xslde".equals(username)&&!"test".equals(username)&&!"xslde.com".equals(username)&&!"admin".equals(username)){
throw new UnknownAccountException("用户不存在!");
}
User user = null;
if ("xslde".equals(username)){
user = new User();
user.setUsername("xslde");
user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
user.setSalt("abcd");
user.setAvailable(1);
}
if ("admin".equals(username)){
user = new User();
user.setUsername("admin");
user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
user.setSalt("abcd");
user.setAvailable(1);
}
if ("test".equals(username)){
user = new User();
user.setUsername("test");
user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
user.setSalt("abcd");
user.setAvailable(0);
} if (user.getAvailable()!=1){
throw new LockedAccountException("账户已被锁定");
}
return new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
} //生成一个加盐密码
public static void main(String[] args){
//加密类型
String hashAlgorithmName = "md5";
//迭代次数
Integer count = 2;
String password = "123456";
String salt = "abcd";
String s = new SimpleHash(hashAlgorithmName,password,salt,count).toHex();
System.out.println(s); }
}
小结两点:第一是理清数据之间关系,即一个用户可以拥有多个角色,而一个角色可以具备多种权限
第二是理解关于盐的事情,因为这里用的密码虽然已经通过MD5算法进行加密,但是假如别人是知道这个系统用的是这个加密的算法,那么很容易就通过对应的密码方式进 行破解,所以引入盐的概念,即生活中一道相同的菜在不同人的手里所下的盐的量都会有所区别,借助这个道理,因为我们上面已经在Realm中配置过加密工具了,所以这里不加盐值的话会默认使用"MD5"算法(上面配置加密工具时设置)进行密码加密,如果加了盐值,在算法加密之后会再进行盐值加密过程。
之后在controller层中,进行如下的配置
package springbootshiro2.demo.action; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import java.util.Map; /**
* 〈一句话功能简述〉<br>
* 〈〉
*
* @author X450J
* @create 2019/5/30
* @since 1.0.0
*/
@Controller
public class PermissionAction { @GetMapping("/unauthorized")
public String unauthorized(){
return "unauthorized.html";
} //拥有user角色才可访问
@GetMapping("/user")
public String user(Map<String,String> code){
code.put("msg","拥有user角色");
return "user.html";
} //拥有user角色才可访问和user:query权限才可访问
@GetMapping("/user/per")
public String userPer(Map<String,String> mode){
mode.put("msg","拥有user角色和user:query权限");
return "user.html";
} //拥有admin角色才可访问
@GetMapping("/admin")
public String admin(Map<String,String> mode){
mode.put("msg","拥有admin角色");
return "admin.html";
} //通过注解控制授权
@RequiresRoles({"admin"})
@GetMapping("/abc")
public String abc(Map<String,String> mode){
mode.put("msg","拥有admin角色,并且是通过注解控制");
return "admin.html";
} //通过注解控制权限
@RequiresPermissions({"user:query"})
@GetMapping("/abcd")
public String abcd(Map<String,String> mode){
mode.put("msg","拥有user:query权限,并且是通过注解控制");
return "user.html";
}
}
参考文章:Spring Boot 整合 Shiro-登录认证和权限管理
Springboot整合Shiro安全框架的更多相关文章
- SpringBoot整合Shiro权限框架实战
什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...
- 补习系列(6)- springboot 整合 shiro 一指禅
目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...
- SpringBoot整合Shiro使用Ehcache等缓存无效问题
前言 整合有缓存.事务的spring boot项目一切正常. 在该项目上整合shiro安全框架,发现部分类的缓存Cache不能正常使用. 然后发现该类的注解基本失效,包括事务Transaction注解 ...
- 转:30分钟了解Springboot整合Shiro
引自:30分钟了解Springboot整合Shiro 前言:06年7月的某日,不才创作了一篇题为<30分钟学会如何使用Shiro>的文章.不在意之间居然斩获了22万的阅读量,许多人因此加了 ...
- springboot整合Shiro功能案例
Shiro 核心功能案例讲解 基于SpringBoot 有源码 从实战中学习Shiro的用法.本章使用SpringBoot快速搭建项目.整合SiteMesh框架布局页面.整合Shiro框架实现用身份认 ...
- SpringBoot 整合Shiro 一指禅
目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...
- SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理|前后端分离(下)----筑基后期
写在前面 在上一篇文章<SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期>当中,我们初步实现了SpringBoot整合Shiro ...
- SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)
导入依赖(pom.xml) <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...
- SpringBoot整合Shiro实现权限控制
目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...
随机推荐
- 2016 Multi-University Training Contest 2 A Acperience
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 题意: 略. 思路: 略....真分数... = =.我今天是纠结去死了.哎,继续加油,就比如gfd说的还有下一场,下下场,不要烦,不要绝望,因为,这算什么? )扔份代 ...
- bzoj 4530: [Bjoi2014]大融合【LCT】
新姿势,一般来讲LCT只能维护splay重边里的数据,而这里要求维护整颗子树的size 多维护一个sq表示当前点轻儿子的size和,si表示包括轻重边的整颗子树的大小 然后需要改sq的地方是link和 ...
- CF126B Password【KMP】By cellur925
题目传送门 其实$Chemist$在之前写了非常棒的题解! 我长话短说,补充两句. “那么当$next[n]$>$max$时显然不能将$next[n]$作为最长子串的长度”这句话其实在说,因为一 ...
- jQuery笔记之工具方法—Ajax 优化回调地狱
在上一篇文我们说到了回调地狱不好的地方,今天我们看看怎么来优化它,让它可以运用到实际开发中. 什么是回调地狱?回调地狱就是一个函数里面嵌套了所有功能函数,然后缩略图形成一个三角形. 这样的代码可复用性 ...
- 「软件」仿站小工具v9.0
仿站小工具是通过网址下载静态网页的工具.从输入的网址下载html代码,提取出JS.Css.Image.Picture.Flash等静态文件网址,再从下载完好的Css代码中提取出Image静态文件网址, ...
- spring cloud feign 调用接口报错"No message available
There was an unexpected error (type=Internal Server Error, status=500). status 404 reading HelloServ ...
- [CQOI2014]通配符匹配
Description 几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户.最常见的通配符有两个,一个是星号(""'),可以匹配0个及以上的任意字符:另一个 ...
- MySQL与RAID
RAID在mysq中适用场景 raid0:由于性能高和成本低,以及基本没有数据恢复的能力,而且它比单片磁盘损坏的概率要高.建议只在不担心数据丢失的情况下使用,如备库(slave)或者某些原因" ...
- 基于udp协议的套接字及udp协议粘包问题
udp协议的套接字 udp协议传输 服务端和客户端没有建立连接一说. import socket # 总结一下基础工作流程:服务端生成套接字并绑定ip_port,进入数据传输循环,服务端接受客户端发 ...
- IO 优化
转自 BlackJack_ #define fastcall __attribute__((optimize("-O3"))) #define IL __inline__ __at ...