Apache Shiro 是一个Java 的安全(权限)框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在Java EE环
境。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。下载地址:http://shiro.apache.org/ 2.有哪些功能?
Authentication:身份认证、登录,验证用户是不是拥有相应的身份;Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;Web Support:Web支持,可以非常容易的集成到Web环境;Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率Concurrency:Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传播过去Testing:提供测试支持;Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。 3.Shiro架构(外部)
从外部来看Shiro,即从应用程序角度来观察如何使用shiro完成工作:
subject: 应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject, Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其 实是一个门面,SecurityManageer 才是实际的执行者SecurityManager:安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它 管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于 SpringMVC的DispatcherServlet的角色Realm:Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager 要验证 用户身份,那么它需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从 Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看成 DataSource; 4.Shiro架构(内部)
Subject:任何可以与应用交互的 ‘用户’;Security Manager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏,所有具体的交互 都通过Security Manager进行控制,它管理者所有的Subject,且负责进行认证,授权,会话,及缓存的管理。Authenticator:负责Subject认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;Authorizer:授权器,即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的那些功能;Realm:可以有一个或者多个的realm,可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm。SessionManager:管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的JavaSE环境中。CacheManager:缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能;Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于密码加密,解密等。 2.Hello,Shiro
查看官网文档:http://shiro.apache.org/tutorial.html官方的quickstart:https://github.com/apache/shiro/tree/master/samples/quickstart/ 1.快速实践 创建一个maven父工程,用于学习Shiro,删掉不必要的东西。创建一个普通的Maven子工程:shiro-01-helloworld。根据官方文档,导入Shiro的依赖。
log4j.rootLogger=INFO, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n# General Apache librarieslog4j.logger.org.apache=WARN# Springlog4j.logger.org.springframework=WARN# Default Shiro logginglog4j.logger.org.apache.shiro=INFO# Disable verbose logginglog4j.logger.org.apache.shiro.util.ThreadContext=WARNlog4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
shiro.ini
[users]# user 'root' with password 'secret' and the 'admin' roleroot = secret, admin# user 'guest' with the password 'guest' and the 'guest' roleguest = guest, guest# user 'presidentskroob' with password '12345' ("That's the same combination on# my luggage!!!" ;)), and role 'president'presidentskroob = 12345, president# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'darkhelmet = ludicrousspeed, darklord, schwartz# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'lonestarr = vespa, goodguy, schwartz# -----------------------------------------------------------------------------# Roles with assigned permissions## Each line conforms to the format defined in the# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc# -----------------------------------------------------------------------------[roles]# 'admin' role has all permissions, indicated by the wildcard '*'admin = *# The 'schwartz' role can do anything (*) with any lightsaber:schwartz = lightsaber:*# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with# license plate 'eagle5' (instance specific id)goodguy = winnebago:drive:eagle5
编写自己的QuickStratimport org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;//import org.apache.shiro.ini.IniSecurityManagerFactory;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.Session;import org.apache.shiro.subject.Subject;//import org.apache.shiro.lang.util.Factory;import org.apache.shiro.util.Factory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): Factory
测试运行一下。
发现,执行完毕什么都没有,可能是maven依赖中的作用域问题,我们需要将scope作用域删掉,
默认是在test,然后重启,那么我们的quickstart就结束了,默认的日志消息!
[main] INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler..、[main] INFO [Quickstart] - Retrieved the correct value! [aValue] [main] INFO [Quickstart] - User [lonestarr] logged in successfully、[main] INFO [Quickstart] - May the Schwartz be with you! [main] INFO [Quickstart] - You may use a lightsaber ring、 Use it wisely、[main] INFO [Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'、 Here are the keys - have fun!
OK! 2.代码解释 导入了一堆包!类的描述通过工厂模式创建SecurityManager的实例对象。
// The easiest way to create a Shiro SecurityManager with configured// realms, users, roles and permissions is to use the simple INI config.// We'll do that by using a factory that can ingest a .ini file and// return a SecurityManager instance:// 使用类路径根目录下的shiro.ini文件// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively):Factory
// get the currently executing user: 获取当前正在执行的用户Subject currentUser = SecurityUtils.getSubject();
session的操作// 用会话做一些事情(不需要web或EJB容器!!!)// Do some stuff with a Session (no need for a web or EJB container!!!)Session session = currentUser.getSession(); //获得sessionsession.setAttribute("someKey", "aValue"); //设置Session的值!String value = (String) session.getAttribute("someKey"); //从session中获取值if (value.equals("aValue")) { //判断session中是否存在这个值! log.info("==Retrieved the correct value! [" + value + "]");}
用户认证功能// 测试当前的用户是否已经被认证,即是否已经登录!// let's login the current user so we can check against roles andpermissions:if (!currentUser.isAuthenticated()) { // isAuthenticated();是否认证 //将用户名和密码封装为 UsernamePasswordToken ; UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); //记住我功能 try { currentUser.login(token); //执行登录,可以登录成功的! } catch (UnknownAccountException uae) { //如果没有指定的用户,则UnknownAccountException异常 log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { //密码不对的异常! log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { //用户被锁定的异常 log.info("The account for username " + token.getPrincipal() + " is locked、" + "Please contact your administrator to unlock it."); } // ..、catch more exceptions here (maybe custom ones specific toyour application? catch (AuthenticationException ae) { //认证异常,上面的异常都是它的子类 //unexpected condition? error? }}//说出他们是谁://say who they are: //打印他们的标识主体(在本例中为用户名)://print their identifying principal (in this case, a username):log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
角色检查// test a role:// 是否存在某一个角色if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!");} else { log.info("Hello, mere mortal.");}
权限检查,粗粒度//测试用户是否具有某一个权限,行为//test a typed permission (not instance-level)if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring、Use it wisely.");} else { log.info("Sorry, lightsaber rings are for schwartz masters only.");}
权限检查,细粒度//测试用户是否具有某一个权限,行为,比上面更加的具体!//a (very powerful) Instance Level permission:if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with licenseplate (id) 'eagle5'、" + "Here are the keys - have fun!");} else { log.info("Sorry, you aren't allowed to drive the 'eagle5'winnebago!");}
注销操作//执行注销操作!//all done - log out!currentUser.logout();
退出系统System.exit(0);3.集成shiro 1.准备工作 搭建一个SpringBoot项目、选中web模块即可!导入Maven依赖thymeleafOK,一个简单的Shiro程序体验结束!!!
首页哦!
package com.github.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","Hello,Shiro!"); return "index"; }}
访问测试! 2.整合Shiro回顾核心API:
Subject:用户主体SecurityManager:安全管理器Realm:Shiro 连接数据
步骤:
导入Shiro 和 spring整合的依赖。package com.github.config;import org.springframework.context.annotation.Configuration;// 声明为配置类@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean // 创建 DefaultWebSecurityManager // 创建 realm 对象}
创建一个realm对象,需要自定义一个realm的类,用来编写一些查询的方法,或者认证与授权的逻辑。package com.github.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;// 自定义Realmpublic class UserRealm extends AuthorizingRealm { // 执行授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行 => 授权逻辑PrincipalCollection"); return null; } // 执行认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了 => 认证逻辑AuthenticationToken"); return null; }}
将这个类注册到我们的Bean中!——ShiroConfig@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean // 创建 DefaultWebSecurityManager // 创建 realm 对象 @Bean public UserRealm userRealm(){ return new UserRealm(); }}
创建DefaultWebSecurityManager// 创建 DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联Realm securityManager.setRealm(userRealm); return securityManager; }
创建ShiroFilterFactoryBean// 创建 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; }
完整的配置:
package com.github.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;// 声明为配置类@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; } // 创建 DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联Realm securityManager.setRealm(userRealm); return securityManager; } // 创建 realm 对象 @Bean public UserRealm userRealm(){ return new UserRealm(); }}
3.页面拦截实现 编写两个页面、在templates目录下新建一个user目录add.html、update.html ADD
update
@Controllerpublic class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","Hello,Shiro!"); return "index"; } @RequestMapping("/user/add") public String toAdd(){ return "user/add"; } @RequestMapping("/user/update") public String toUpdate(){ return "user/update"; }}
在index页面上,增加跳转链接 首页哦!
add | update
// 创建 ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); Map
登录
@RequestMapping("/toLogin")public String toLogin(){ return "login";}
在shiro中配置一下! ShiroFilterFactoryBean() 方法下面 再次测试,成功的跳转到了我们指定的Login页面! 优化一下,使用通配符来操作! 测试一下! 4.登录认证操作 编写登录的controller// 登陆操作 @RequestMapping("/login") public String login(String username,String password,Model model){ // 使用shiro,编写认证操作 // 1、获取Subject Subject subject = SecurityUtils.getSubject(); // 2、封装用户的数据 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 3、执行登录的方法,只要没有异常就代表登录成功! try { // 登录成功!返回首页 subject.login(token); return "index"; // 用户名不存在 } catch (UnknownAccountException e) { model.addAttribute("msg","用户名不存在"); return "login"; // 密码错误 } catch (IncorrectCredentialsException e) { model.addAttribute("msg","密码错误"); return "login"; } }
在前端修改对应的信息输出或者请求!登录页面增加一个msg提示:
给表单增加一个提交地址:
测试一下: 在UserRealm中编写用户认证的判断逻辑:
// 执行认证逻辑@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了 => 认证逻辑AuthenticationToken"); // 数据库的用户名和密码 String name = "root"; String password = "root"; // 1.判断用户名 UsernamePasswordToken userToken = (UsernamePasswordToken)token; if (!userToken.getUsername().equals(name)){ // 用户名不存在 // shiro底层就会抛出 UnknownAccountException return null; } // 2、验证密码,我们可以使用一个AuthenticationInfo实现类SimpleAuthenticationInfo // shiro会自动帮我们验证!重点是第二个参数就是要验证的密码! return new SimpleAuthenticationInfo("", password, "");}
测试一下! 5.整合数据库CREATE DATAbase `mybatis`;USE `mybatis`;CREATE TABLE `user`(`id` INT(20) NOT NULL PRIMARY KEY,`name` VARCHAR(30) DEFAULT NULL,`pwd` VARCHAR(30) DEFAULT NULL)ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `user`(`id`,`name`,`pwd`) VALUES(1,'subei','123456'),(2,'张三','123456'),(3,'李四','123456');
导入Mybatis相关依赖spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #SpringBoot默认是不注入这些的,需要自己绑定 #druid数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity #则导入log4j 依赖就行 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
编写mybatis的配置——application.properties# 别名配置mybatis.type-aliases-package=com.github.pojomybatis.mapper-locations=classpath:mapper/*.xml
编写实体类,引入Lombok@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private int id; private String name; private String pwd;}
编写Mapper接口@Repository@Mapperpublic interface UserMapper { public User queryUserByName(String name);}
编写Mapper配置文件<?xml version="1.0" encoding="UTF-8" ?>
public interface UserService { public User queryUserByName(String name);}
@Servicepublic class UserServiceImpl implements UserService{ @Autowired UserMapper mapper; @Override public User queryUserByName(String name) { return mapper.queryUserByName(name); }}
测试一下,保证能够从数据库中查询出来。@SpringBootTestclass SpringbootShiroApplicationTests { @Autowired UserServiceImpl userService; @Test void contextLoads() { System.out.println(userService.queryUserByName("root")); }}
改造UserRealm,连接到数据库进行真实的操作!// 自定义Realmpublic class UserRealm extends AuthorizingRealm { @Autowired UserService userService; // 执行授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); return null; } // 执行认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了=>认证逻辑AuthenticationToken"); UsernamePasswordToken userToken = (UsernamePasswordToken)token; // 真实连接数据库 User user = userService.queryUserByName(userToken.getUsername()); if (user==null){ // 用户名不存在 return null; // shiro底层就会抛出 UnknownAccountException } return new SimpleAuthenticationInfo("", user.getPwd(), ""); }}
测试成功!
思考:密码比对原理探究
这个Shiro,是怎么帮我们实现密码自动比对的呢?
去 realm的父类 AuthorizingRealm 的父类 AuthenticatingRealm 中找一个方法;核心: getCredentialsMatcher() 翻译过来:获取证书匹配器;去看这个接口 CredentialsMatcher 有很多的实现类,MD5盐值加密;
如何把一个字符串加密为MD5;替换当前的Realm 的 CredentialsMatcher 属性,直接使用 Md5CredentialsMatcher 对象, 并设置加密算法;密码一般都不能使用明文保存?
需要加密处理;思路分析
// 密码验证// 加密 md5HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 加密算法的名称hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 是否让它 进行16进制的编码hashedCredentialsMatcher.isStoredCredentialsHexEncoded();// 迭代的次数// hashedCredentialsMatcher.setHashIterations(3);SimpleHash simpleHash = new SimpleHash("MD5",user.getPwd() );String s = simpleHash.toHex();return new SimpleAuthenticationInfo("",s,"");
6.用户授权操作在ShiroFilterFactoryBean中添加一个过滤器:使用shiro的过滤器来拦截请求即可!
// 授权过滤器filterMap.put("/user/add","perms[user:add]"); // 大家记得注意顺序!
再次启动测试一下,访问add,发现以下错误!未授权错误!
注意:当我们实现权限拦截后,shiro会自动跳转到未授权的页面,但没有这个页面,所有401了;
配置一个未授权的提示的页面,增加一个controller提示;
@RequestMapping("/noauth")@ResponseBodypublic String noAuth(){ return "未经授权不能访问此页面";}
然后再 shiroFilterFactoryBean 中配置一个未授权的请求页面!
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
测试,现在没有授权,可以跳转到我们指定的位置了! 7.用户授权操作在UserRealm 中添加授权的逻辑,增加授权的字符串!
// 执行授权逻辑@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); // 给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加资源的授权字符串 info.addStringPermission("user:add"); return info;}
再次登录测试,发现登录的用户是可以进行访问add 页面了!授权成功!
修改实体类,增加一个字段问题,我们现在完全是硬编码,无论是谁登录上来,都可以实现授权通过,但是真实的业务情况应该 是,每个用户拥有自己的一些权限,从而进行操作,所以说,权限,应该在用户的数据库中,正常的情 况下,应该数据库中是由一个权限表的,我们需要联表查询,但是这里为了大家操作理解方便一些,我 们直接在数据库表中增加一个字段来进行操作!
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private int id; private String name; private String pwd; private String perms;}
在自定义的授权认证中,获取登录的用户,从而实现动态认证授权操作!
在用户登录授权的时候,将用户放在 Principal 中,改造下之前的代码
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
然后再授权的地方获得这个用户,从而获得它的权限
// 执行授权逻辑@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); // 给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加资源的授权字符串 // info.addStringPermission("user:add"); // 获得当前对象 Subject subject = SecurityUtils.getSubject(); // 拿到User对象 User currentUser = (User) subject.getPrincipal(); // 设置权限 info.addStringPermission(currentUser.getPerms()); return info;}
给数据库中的用户增加一些权限 在过滤器中,将update请求也进行权限拦截下启动测试,登录不同的账户,进行测试一下!测试完美通过OK! 8.整合Thymeleaf
添加Maven的依赖;根据权限展示不同的前端页面
// 配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用@Beanpublic ShiroDialect getShiroDialect(){ return new ShiroDialect();}
修改前端的配置 首页哦!
add update
Subject subject = SecurityUtils.getSubject();subject.getSession().setAttribute("loginUser",user);
前端从session中获取,然后用来判断是否显示登录。 登录