SpringBoot集成Shiro安全框架
SpringBoot集成Shiro安全框架
- 1.shiro的定义
- 2.SpringBoot集成shiro的步骤
- 3.完成的效果
1.shiro的定义
1.shiro的作用
认证、授权、加密、会话管理、Web集成、缓存
2.shiro的名词
Authentication:身份认证/登录,验证用户是不是拥有相应的身份
Authorization:授权,即权限 管理,验证某个人已经登录的人是否拥有某些权限
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前所有的信息都是在会话中。。
Cryptography:加密,保护数据的安全性,密码加密
Web Support:Web支持,可以非常容易的集成到web环境
Caching:缓存,比如用户登录之后,他的用户信息,权限不必每次去查
Concurrency:shiro支持多线程应用兵法验证,即如果在一个线程中开启另外一个线程,权限会自动传递过去
Testing:提供测试支持
Run AS:允许一个用户假装另一个用户(如果他们允许)的身份进行访问
Remember Me:记住我,这是一个常见的功能,即一次登录后,下次再来的话就不用登录了
记住一点,Shiro不会去维护用户、维护权限:这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro即可!
3.shiro的架构
Subject,Subject其实代表的就是当前正在执行操作的用户,只不过因为“User”一般指代人,但是一个“Subject”可以是人,也可以是任何的第三方系统,服务账号等任何其他正在和当前系统交互的第三方软件系统。
所有的Subject实例都被绑定到一个SecurityManager,如果你和一个Subject交互,所有的交互动作都会被转换成Subject与SecurityManager的交互。
SecurityManager。SecurityManager是Shiro的核心,他主要用于协调Shiro内部各种安全组件,不过我们一般不用太关心SecurityManager,对于应用程序开发者来说,主要还是使用Subject的API来处理各种安全验证逻辑。
Realm,这是用于连接Shiro和客户系统的用户数据的桥梁。一旦Shiro真正需要访问各种安全相关的数据(比如使用用户账户来做用户身份验证以及权限验证)时,他总是通过调用系统配置的各种Realm来读取数据。
2.SpringBoot集成shiro的步骤
1.添加maven依赖
<!-- shiro整合springboot所需相关依赖--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency><!-- 兼容于thymeleaf的shiro --><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency><!--end.......-->
2.新建缓存文件
ehcache-shiro.xml
<ehcache updateCheck="false" name="cacheManagerConfigFile"><!--name:缓存名称。maxElementsInMemory:缓存最大个数。eternal:对象是否永久有效,一但设置了,timeout将不起作用。timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。maxElementsOnDisk:硬盘最大缓存个数。diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush:内存数量最大时是否清除。--><defaultCache maxElementsInMemory="10000" eternal="false"timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false"diskPersistent="false" diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"/><!-- 登录记录程序锁定1分钟 --><cache name="shiro-activeSessionCache" eternal="false"maxElementsInMemory="10000" overflowToDisk="false" timeToIdleSeconds="0"timeToLiveSeconds="0" statistics="true"/>
</ehcache>
3.重要的shiro配置类
不需要完全记住,只需要修改其中的一小部分
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
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.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;/*** @ Author :Zgq* @ Date :Created in 18:22 2019/6/11* @ Description:shiro的配置类* @ Modified By:* @Version: $*/
@Configuration
public class ShiroConfig {/**唯一需要修改的地方,有注释* ShiroFilterFactoryBean 处理拦截资源文件问题。* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,* 因为在初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager* Filter Chain定义说明* 1、一个URL可以配置多个Filter,使用逗号分隔* 2、当设置多个过滤器时,全部验证通过,才视为通过* 3、部分过滤器可指定参数,如perms,roles*/@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 必须设置 SecurityManagershiroFilterFactoryBean.setSecurityManager(securityManager);// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面//访问的是后端url地址为 /login的接口,/未登录页面,会跳转到登录页面shiroFilterFactoryBean.setLoginUrl("/login");// 登录成功后要跳转的链接shiroFilterFactoryBean.setSuccessUrl("/index");// 未授权界面;,基于AOP拦截,都会到登录页面shiroFilterFactoryBean.setUnauthorizedUrl("/login");// 拦截器.Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();// 配置不会被拦截的链接 顺序判断,anon放开,不会拦截,authc会拦截//静态资源不能被拦截filterChainDefinitionMap.put("/assets/**", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/images/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/login", "anon");filterChainDefinitionMap.put("/userLogin", "anon");// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put("/logout", "logout");//配置某个url需要某个权限码filterChainDefinitionMap.put("/hello", "perms[how_are_you]");// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->filterChainDefinitionMap.put("/**", "authc");//配置记住我或认证通过可以访问的地址filterChainDefinitionMap.put("/index", "user");
// filterChainDefinitionMap.put("/", "user");System.out.println("Shiro拦截器工厂类注入成功");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 缓存* @return*/@Beanpublic EhCacheManager getEhCacheCache() {EhCacheManager em = new EhCacheManager();em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");return em;}/*** 代理* @return*/@Beanpublic DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();daap.setProxyTargetClass(true);return daap;}@Beanpublic DefaultWebSessionManager getDefaultWebSessionManager() {DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();defaultWebSessionManager.setSessionDAO(getMemorySessionDAO());defaultWebSessionManager.setGlobalSessionTimeout(1 * 60 * 60 * 1000);defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);defaultWebSessionManager.setSessionIdCookieEnabled(true);defaultWebSessionManager.setSessionIdCookie(getSimpleCookie());return defaultWebSessionManager;}@Beanpublic MemorySessionDAO getMemorySessionDAO() {MemorySessionDAO memorySessionDAO = new MemorySessionDAO();memorySessionDAO.setSessionIdGenerator(javaUuidSessionIdGenerator());return memorySessionDAO;}@Beanpublic JavaUuidSessionIdGenerator javaUuidSessionIdGenerator() {return new JavaUuidSessionIdGenerator();}/*** cookie对象* @return*/@Beanpublic SimpleCookie getSimpleCookie() {SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("security.session.id");//<!-- 记住我cookie生效时间30天 ,单位秒;-->simpleCookie.setMaxAge(259200);simpleCookie.setPath("/");return simpleCookie;}/*** cookie管理对象;* @return*//*@Beanpublic CookieRememberMeManager rememberMeManager(){System.out.println("ShiroConfiguration.rememberMeManager()");CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(getSimpleCookie());return cookieRememberMeManager;}*/@Beanpublic LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 注入 securityManager*/@Bean(name = "securityManager")public DefaultWebSecurityManager getDefaultWebSecurityManager(ShiroRealm shiroRealm) {DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();// 关联realm.dwsm.setRealm(shiroRealm);//用户授权/认证信息Cache,采用EhCache缓存dwsm.setCacheManager(getEhCacheCache());dwsm.setSessionManager(getDefaultWebSessionManager());//注入记住我管理器;/* dwsm.setRememberMeManager(rememberMeManager());*/return dwsm;}@Beanpublic ShiroRealm shiroRealm(EhCacheManager cacheManager) {ShiroRealm shiroRealm = new ShiroRealm();shiroRealm.setCacheManager(cacheManager);return shiroRealm;}//开启shiro注解支持@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(ShiroRealm shiroRealm){AuthorizationAttributeSourceAdvisor aasa =new AuthorizationAttributeSourceAdvisor();aasa.setSecurityManager(getDefaultWebSecurityManager(shiroRealm));return aasa;}/*** 配置前台页面thymeleaf页面的标签* @return*/@Beanpublic ShiroDialect shiroDialect() {return new ShiroDialect();}}
4.核心的授权认证类
import com.example.echart.entity.Permission;
import com.example.echart.entity.Role;
import com.example.echart.entity.User;
import com.example.echart.mapper.UserRoleMapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** @ Author :Zgq* @ Date :Created in 18:19 2019/6/11* @ Description:Shiro中最主要的代码,核心代码,用户认证授权处* @ Modified By:* @Version: $*/
public class ShiroRealm extends AuthorizingRealm {@Autowiredprivate SessionDAO sessionDAO;@Autowiredprivate UserRoleMapper userRoleMapper;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection auth) {//授权String username = (String)auth.getPrimaryPrincipal();System.out.println("进入到授权Realm中:"+username);List<Role> dbroleList = userRoleMapper.selectRoleList(username);List<String> roleList=new ArrayList<String>();for(Role r:dbroleList){roleList.add(r.getCode());}List<Permission> dbpermissions = userRoleMapper.selectPermissionList(username);List<String> permissionList=new ArrayList<String>();for(Permission p:dbpermissions){permissionList.add(p.getPermission());}// roleList.add("ADMIN");
// roleList.add("USER");// List<String> permissionList=new ArrayList<String>();
// permissionList.add("ADMIN:USER:UPDATA");
// permissionList.add("ADMIN:USER:DELETE");SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();simpleAuthorizationInfo.addRoles(roleList);simpleAuthorizationInfo.addStringPermissions(permissionList);return simpleAuthorizationInfo;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {//认证String username = (String)auth.getPrincipal();System.out.println("进入到认证Realm中:"+username);//在认证之前判断当前登录用户,只允许一个账号登录Collection<Session> sessions = sessionDAO.getActiveSessions();for (Session session : sessions){String loginedUsername = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));if(username.equals(loginedUsername)){session.setTimeout(0);break;}}//通过username在数据库中查询用户,判断密码User dbuser = userRoleMapper.selectByUserName(username);//通过用户名在数据库中拿到,判断用户名和密码对不对if(dbuser!=null){SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(username, dbuser.getPwd(), "userRealm");return authInfo;}return null;}
}
5.登录的Controller类,LoginController
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;/*** @ Author :Zgq* @ Date :Created in 10:30 2019/6/12* @ Description:登录层方法* @ Modified By:* @Version: $*/@Controller
public class LoginController {//登录页面@RequestMapping(value = {"/login"})public String login(Map<String,String> map) {map.put("msg","请登录");return "/shiro/login-page";}//登录成功页面@RequestMapping(value = {"/index"})public String index(Map<String,String> map) {map.put("msg","登录成功");//获取用户信息Subject subject = SecurityUtils.getSubject();String username = (String)subject.getPrincipal();map.put("username",username);return "/shiro/index";}//登录请求@RequestMapping(value = {"/userLogin"})public String userLogin(String username, String pwd,boolean rememberMe, Map<String,String> map){Subject subject = SecurityUtils.getSubject();//根据自己盐加密的方式,放入密码String encodePwd = new Md5Hash(pwd, username).toString();UsernamePasswordToken auth = new UsernamePasswordToken(username, encodePwd,rememberMe);try {subject.login(auth);return "redirect:/index";}catch (Exception e){e.printStackTrace();map.put("msg","账号或密码错误");return "redirect:/login";}}//退出登录@RequestMapping(value = {"/loginOut"})public String loginOut(Map<String,String> map) {//获取用户信息Subject subject = SecurityUtils.getSubject();subject.logout();map.put("msg","退出登录");return "redirect:/login";}/*** 加密的测试* @param args*/public static void main(String[] args) {//加密String zhouguoqing = new Md5Hash("111", "admin1").toString();System.out.println(zhouguoqing);}
}
6.因为是和数据库直接对接的,所以我也新建了一个UserRoleMapper接口,返回数据
import com.example.echart.entity.Permission;
import com.example.echart.entity.Role;
import com.example.echart.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;import java.util.List;@Mapper
@Component
public interface UserRoleMapper {/*** 通过登录名查询信息* @param username* @return* @throws Exception*/@Select("select * from t_user where username=#{username} limit 1")User selectByUserName(String username);/*** 通过用户名查询用户角色信息* @param username* @return* @throws Exception*/@Select("select * from t_user u,t_role r,t_user_role ur where u.username=#{username} and ur.userId=u.id and ur.roleId=r.id")List<Role> selectRoleList(String username);/*** 通过用户名查找用户权限* @param username* @return*/@Select("select * from t_permission p \n" +"where p.id in(\n" +"\tselect permissionId from t_role_permission rp \n" +"\twhere rp.roleId in (\n" +"\t\t\tselect ur.roleId from t_user_role ur where userId in(\n" +"\t\t\tselect u.id from t_user u where u.username=#{username}\n" +"\t\t\t)\n" +"\t)\n" +")")List<Permission> selectPermissionList(String username);}
7.前台页面thymeleaf的展示
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"xmlns:shiro="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><span th:text="${msg}"></span>
你好:<span th:text="${username}"></span><form action="/loginOut" method="post"><input type="submit" value="退出">
</form><p shiro:hasRole="ADMIN">ADMIN角色</p>
<p shiro:hasRole="USER">USER角色</p>
<p shiro:hasRole="SUPERMANE">SUPERMAN角色</p><p shiro:hasPermission="ADMIN:USER:UPDATA">UPDATA权限</p>
<p shiro:hasPermission="ADMIN:USER:DELETE">DELETE权限</p>
<p shiro:hasPermission="ADMIN:USER:INSERT">INSERT权限</p>
<p shiro:hasPermission="ADMIN:USER:SELECT">SELECT权限</p>
</body>
</html>
3.完成的效果
用不同用户登录之后会自动获取登录用户的角色和权限信息
学习地址:https://www.bilibili.com/video/av44084437
SpringBoot集成Shiro安全框架相关推荐
- springboot集成shiro实现用户登录认证
Apache Shiro 是一个功能强大且易于使用的Java安全框架,可执行身份验证.授权.加密和会话管理.使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大 ...
- php 配置文件加密工具类,SpringBoot集成Jasypt安全框架以及配置文件内容加密(代码示例)...
本篇文章给大家带来的内容是关于SpringBoot集成Jasypt安全框架以及配置文件内容加密(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我们在SpringBoot项 ...
- springboot集成shiro 前后端分离 统一处理shiro异常
springboot集成shiro 前后端分离 统一处理shiro异常 参考文章: (1)springboot集成shiro 前后端分离 统一处理shiro异常 (2)https://www.cnbl ...
- SpringBoot集成权限认证框架(Sa-Token)
SpringBoot集成权限认证框架(Sa-Token) 介绍 身份验证又称"验证"."鉴权",是指通过一定的手段,完成对用户身份的确认. 身份验证的目的是确认 ...
- apache shiro jar包_只需要6个步骤,springboot集成shiro,并完成登录
小Hub领读: 导入jar包,配置yml参数,编写ShiroConfig定义DefaultWebSecurityManager,重写Realm,编写controller,编写页面,一气呵成.搞定,是个 ...
- Springboot集成Shiro+Redis后,@Transactional注解不起作用
为什么80%的码农都做不了架构师?>>> 使用Springboot构建 mybatis+Shiro+Redis+Druid 的前后端分离web项目, 具体可以参考博客https ...
- (课堂作业)spring-boot集成shiro的步骤及代码解析
1.创建一个简单的Springboot项目,包含shiro和mybatis-plus. 2.集成shiro 2.1引入依赖 <dependency><groupId>org.a ...
- 【Springboot学习】SpringBoot集成Shiro前后端分离使用redis做缓存【个人博客搭建】
shiro-redis 目录 shiro-redis 下载 shiro-core/jedis 版本对比图 使用前 如何配置? 设置文件 Redis 独立 Redis哨兵 Redis 集群 Spring ...
- Springboot 集成 Shiro 入门学习
文章目录 Shiro概述 主要特征 shiro如何工作 官方快速开始代码分析 Springboot整合Shiro 导入依赖 创建配置类ShiroConfig 创建Realm 创建控制器 创建静态页面 ...
最新文章
- 技嘉主板bios设置键盘不能用_BIOS不再硬梆梆、全新技嘉主板BIOS设置就算不是玩家也能轻松搞定...
- js 对象深拷贝_这一次,彻底理解JavaScript深拷贝
- 用 vue 写小程序,基于 mpvue 框架重写 weui
- 905. 按奇偶排序数组
- 知乎超高赞:都有哪些习惯值得长期坚持?
- 两年Java工作经验应该会些什么技术
- 浏览器访问网页的详细内部过程
- 服务器主动发送fin信号,tcp 服务器向客户端发送FIN
- 智能优化算法:萤火虫算法-附代码
- selenium下载或保存图片最好的方法
- Adobe Premiere基础-常用的视频特效(裁剪,黑白,剪辑速度,镜像,镜头光晕)(十五)
- 阿里巴巴编码规范习题
- 组建团队和调整团队结构
- html编码器是什么意思,编码器是什么意思
- 微信预览wx.previewImage黑屏
- 怎么把知网下载的caj格式论文转成wrod格式呢
- java输出hello java_eclipse输出Hello World的实现方法
- 在线引流工具Tcpcopy原理、使用、采坑
- 熊猫的python小课_朋友圈里那个可爱的小熊猫Python编程的学习笔记,学编程,不难!...
- 联合概率数据互联(JPDA) ----多假设跟踪(MHT)