广州建设银行分行招聘网站,唐山炎黄宽带网站,专业设计网站排名,电商网站有什么特点SpringBoot教程#xff08;三十#xff09; | SpringBoot集成Shiro权限框架 一、 什么是Shiro二、Shiro 组件核心组件其他组件 三、流程说明shiro的运行流程 四、SpringBoot 集成 Shiro #xff08;shiro-spring-boot-web-starter方式#xff09;1. 添加 Shiro 相关 maven2… SpringBoot教程三十 | SpringBoot集成Shiro权限框架 一、 什么是Shiro二、Shiro 组件核心组件其他组件 三、流程说明shiro的运行流程 四、SpringBoot 集成 Shiro shiro-spring-boot-web-starter方式1. 添加 Shiro 相关 maven2. 添加 其他 maven3. 设计数据库表4. 使用mybatisplus逆向工程生成代码5. 添加 自定义Realm 类扩展授权类方法 对应的 注解 6. 添加 ShiroConfig 配置类扩展Shiro过滤机制表 7. 开始测试—后端1. 创建一个LoginController 类2. 创建一个UserController 类 8. 开始测试—前端1. 编写login.html页面2. 编写index.html页面3. 编写403页面 10、测试遇到的问题 五、SpringBoot 集成 Shiro shiro-spring-boot-web-starter方式1. 添加 maven 依赖2. 添加Shiro的配置3. 添加 ShiroConfig 配置类 一、 什么是Shiro
shiro是apache的一个开源框架是一个权限管理的框架实现 用户认证、用户授权。
spring中有spring security (原名Acegi)是一个权限框架它和spring依赖过于紧密没有shiro使用简单。
shiro不依赖于springshiro不仅可以实现 web应用的权限管理还可以实现c/s系统分布式系统权限管理shiro属于轻量框架越来越多企业项目开始使用shiro。
使用shiro实现系统的权限管理有效提高开发效率从而降低开发成本。
二、Shiro 组件
核心组件
主要有三大组件 为 Subject、SecurityManager、 Realms
组件名称概念作用Subject主体可以是用户也可以是程序主体要访问系统系统需要对主体进行认证、授权SecurityManager安全管理器主体进行认证和授权都是通过securityManager进行。它管理着所有 Subject可以看出它是 Shiro 的核心它负责与后边介绍的其他组件进行交互如果学习过 SpringMVC你可以把它看成 DispatcherServlet 前端控制。Realm域领域相当于数据源开发者可自定义的模块根据项目的需求验证和授权的逻辑在 Realm 中实现
其他组件
组件名称概念作用Authenticator认证器主体进行认证最终通过authenticator进行的。Authorizer授权器主体进行授权最终通过authorizer进行的。SessionManagerweb应用中一般是用web容器对session进行管理所以shiro也提供一套session管理的方式。SessionDao通过SessionDao管理session数据针对个性化的session数据存储需要使用sessionDao。Cache Manager缓存管理器主要对session和授权数据进行缓存。比如将授权数据通过cacheManager进行缓存管理和ehcache整合对缓存数据进行管理。DefaultWebSecurityManager安全管理器开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。ShiroFilterFactoryBean过滤器工厂负责创建并配置过滤链filter chain。这些过滤器可以应用于HTTP请求以实现访问控制。
三、流程说明
shiro的运行流程 Shiro把用户的数据封装成标识tokentoken一般封装着用户名密码等信息使用Subject主体获取到封装着用户的数据的标识tokenSubject把标识token交给SecurityManager在SecurityManager安全中心SecurityManager把标识token委托给认证器Authenticator进行身份证。认证器的作用是一般用来指定如何验证它规定本次认证用到那些Realm认证器Authenticator将传入的标识token与数据源Realm对比验证token是否合法
四、SpringBoot 集成 Shiro shiro-spring-boot-web-starter方式
1. 添加 Shiro 相关 maven !--shiro--dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion1.12.0/version/dependency
!-- dependency--
!-- groupIdorg.apache.shiro/groupId--
!-- artifactIdshiro-core/artifactId--
!-- version1.12.0/version--
!-- /dependency--
!-- dependency--
!-- groupIdorg.apache.shiro/groupId--
!-- artifactIdshiro-web/artifactId--
!-- version1.12.0/version--
!-- /dependency--
2. 添加 其他 maven !-- lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId!--provided 意味着打包的时候可以不用包进去--scopeprovided/scope/dependency!-- MySQL JDBC驱动 --dependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactId/dependency!--mybatis-plus 这个版本需要指定了因为场景启动器里面没有 --dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.7/version/dependency!-- MyBatis-Plus代码生成器逆向工程--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-generator/artifactIdversion3.5.7/version/dependency!-- MyBatis-Plus代码生成器所需引擎模板引擎来生成代码文件 --dependencygroupIdorg.freemarker/groupIdartifactIdfreemarker/artifactId/dependency!-- Thymeleaf 用于访问html文件 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependency!--在Thymeleaf中使用shiro标签--dependencygroupIdcom.github.theborakompanioni/groupIdartifactIdthymeleaf-extras-shiro/artifactIdversion2.0.0/version/dependency3. 设计数据库表
用户表、角色表、权限表、用户角色权限、角色权限表
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user (id int NOT NULL AUTO_INCREMENT,username varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL,password varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL,create_time datetime NULL DEFAULT NULL,status int NULL DEFAULT NULL COMMENT 状态,PRIMARY KEY (id) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 3 CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ROW_FORMAT COMPACT;-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO tb_user VALUES (1, 超级小明, 12345, NULL, NULL);
INSERT INTO tb_user VALUES (2, 王子, 12345, NULL, NULL);-- ----------------------------
-- Table structure for tb_role
-- ----------------------------
DROP TABLE IF EXISTS tb_role;
CREATE TABLE tb_role (id int NOT NULL AUTO_INCREMENT,name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 角色名称,description varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 描述,PRIMARY KEY (id) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 3 CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ROW_FORMAT COMPACT;-- ----------------------------
-- Records of tb_role
-- ----------------------------
INSERT INTO tb_role VALUES (1, 超级管理员, NULL);
INSERT INTO tb_role VALUES (2, 产品管理员, NULL);
INSERT INTO tb_role VALUES (3, 订单管理员, NULL);-- ----------------------------
-- Table structure for tb_permission
-- ----------------------------
DROP TABLE IF EXISTS tb_permission;
CREATE TABLE tb_permission (id int NOT NULL AUTO_INCREMENT,url varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL,name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL,PRIMARY KEY (id) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 4 CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ROW_FORMAT COMPACT;-- ----------------------------
-- Records of tb_permission
-- ----------------------------
INSERT INTO tb_permission VALUES (1, 查询, user:queryAll);
INSERT INTO tb_permission VALUES (2, 增加, user:add);
INSERT INTO tb_permission VALUES (3, 删除, user:delete);-- ----------------------------
-- Table structure for tb_user_role
-- ----------------------------
DROP TABLE IF EXISTS tb_user_role;
CREATE TABLE tb_user_role (role_id int NOT NULL COMMENT 角色id,user_id int NOT NULL COMMENT 用户id
) ENGINE InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ROW_FORMAT COMPACT;-- ----------------------------
-- Records of tb_user_role
-- ----------------------------
INSERT INTO tb_user_role VALUES (1, 1);
INSERT INTO tb_user_role VALUES (2, 2);-- ----------------------------
-- Table structure for tb_role_permission
-- ----------------------------
DROP TABLE IF EXISTS tb_role_permission;
CREATE TABLE tb_role_permission (role_id int NOT NULL COMMENT 角色id,permission_id int NOT NULL COMMENT 权限id
) ENGINE InnoDB CHARACTER SET utf8mb3 COLLATE utf8mb3_bin ROW_FORMAT COMPACT;-- ----------------------------
-- Records of tb_role_permission
-- ----------------------------
INSERT INTO tb_role_permission VALUES (1, 1);
INSERT INTO tb_role_permission VALUES (1, 2);
INSERT INTO tb_role_permission VALUES (1, 3);
INSERT INTO tb_role_permission VALUES (2, 1);
INSERT INTO tb_role_permission VALUES (3, 1);4. 使用mybatisplus逆向工程生成代码 具体操作我这边就不具体说了 5. 添加 自定义Realm 类 自定义Realm实现类 要求 1.自定义Realm时通常会继承AuthorizingRealm类因为它同时提供了认证和授权的功能 2.重写 认证方法 doGetAuthenticationInfo() 和 授权方法doGetAuthorizationInfo() 分别用于实现自定义的认证逻辑和授权逻辑。 细节查询资料得知shiro在subject.login(token)方法时不会执行doGetAuthorizationInfo方法只有在访问到有权限验证的接口时会调用查看权限
package com.example.springbootshiro.realm;import com.example.springbootshiro.entity.Permission;
import com.example.springbootshiro.entity.Role;
import com.example.springbootshiro.entity.User;
import com.example.springbootshiro.mapper.RolePermissionMapper;
import com.example.springbootshiro.mapper.UserMapper;
import com.example.springbootshiro.mapper.UserRoleMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** ClassName MyShiroRealm* */
Service
public class MyShiroRealm extends AuthorizingRealm {Autowiredprivate UserMapper userMapper;Autowiredprivate UserRoleMapper userRoleMapper;Autowiredprivate RolePermissionMapper rolePermissionMapper;/*** 认证方法登录认证* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//获取用户输入的用户名密码String username (String) token.getPrincipal();String passwordnew String((char[])token.getCredentials());System.out.println(用户输入---username:username--password:password);//在数据库中查询User userInfouserMapper.selectByName(username);if (userInfo null) {throw new UnknownAccountException(用户名或密码错误);}if (!password.equals(userInfo.getPassword())) {throw new IncorrectCredentialsException(用户名或密码错误);}SimpleAuthenticationInfo authenticationInfo new SimpleAuthenticationInfo(userInfo, // 用户名userInfo.getPassword(), // 密码getName() // realm name);return authenticationInfo;}/*** 授权方法获取用户角色和权限* param principal* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {if(principal null){throw new AuthorizationException(principals should not be null);}User userInfo (User) SecurityUtils.getSubject().getPrincipal();System.out.println(用户--userInfo.getUsername()获取权限中);SimpleAuthorizationInfo simpleAuthorizationInfo new SimpleAuthorizationInfo();//用户获取角色集ListRole roleListuserRoleMapper.findByUserName(userInfo.getUsername());SetString roleSetnew HashSet();for (Role r:roleList){Integer roleIdr.getId();//获取角色idsimpleAuthorizationInfo.addRole(r.getName());//添加角色名字ListPermission permissionListrolePermissionMapper.findByRoleId(roleId);for (Permission p:permissionList){//添加权限simpleAuthorizationInfo.addStringPermission(p.getName());}}System.out.println(角色为- simpleAuthorizationInfo.getRoles());System.out.println(权限为- simpleAuthorizationInfo.getStringPermissions());return simpleAuthorizationInfo;}}扩展授权类方法 对应的 注解 授权类SimpleAuthorizationInfo 方法对应的 注解 角色相关的方法及注解
addRole(String roleName): 添加一个角色到授权信息中。addRoles(CollectionString roleNames): 批量添加多个角色到授权信息中。
相匹配注解
RequiresRoles(value {admin, user}, logical Logical.AND): 表示方法调用者必须同时拥有admin和user两个角色。logical属性决定了多个角色或权限之间的逻辑关系如AND、OR。
权限相关的方法及注解
addStringPermission(String permissionString): 添加一个权限字符串到授权信息中。addStringPermissions(CollectionString permissions): 批量添加多个权限字符串到授权信息中。
相匹配注解
RequiresPermissions(value {user:create, user:update}, logical Logical.OR): 表示方法调用者至少需要拥有user:create或user:update中的一个权限。
其他类别的注解 RequiresAuthentication: 表示方法调用者必须已经通过身份验证。 RequiresUser: 表示方法调用者必须是一个已识别的用户但不一定是特定的角色或拥有特定的权限。
6. 添加 ShiroConfig 配置类
package com.example.springbootshiro.config;import com.example.springbootshiro.realm.MyShiroRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
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.Cookie;
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;/*** ClassName ShiroConfig**/
Configuration
public class ShiroConfig {/*** ShiroFilter是整个Shiro的入口点用于拦截需要安全控制的请求进行处理* 是shiro的大管家相当于mybatis里的SqlSessionFactoryBean* param securityManager* return*/Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean new ShiroFilterFactoryBean();//配置 登录页面URLshiroFilterFactoryBean.setLoginUrl(/login);//配置 登录成功后的页面URLshiroFilterFactoryBean.setSuccessUrl(/index);//配置 访问没有权限的时 触发的页面URLshiroFilterFactoryBean.setUnauthorizedUrl(/403);//页面权限控制配置 Shiro 的过滤器链shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());//设置 Shiro 的安全管理器shiroFilterFactoryBean.setSecurityManager(securityManager);return shiroFilterFactoryBean;}/*** web应用管理配置* param shiroRealm* param cacheManager* param manager* return*/Beanpublic DefaultWebSecurityManager securityManager(Realm shiroRealm, CacheManager cacheManager, RememberMeManager manager) {DefaultWebSecurityManager securityManager new DefaultWebSecurityManager();//设置缓存管理器securityManager.setCacheManager(cacheManager);//设置记住我管理器用于处理用户的“记住我”功能。也就是记住CookiesecurityManager.setRememberMeManager(manager);//设置单个 Realm用于用户认证和授权。securityManager.setRealm(shiroRealm);//设置会话管理器用于管理用户的会话。securityManager.setSessionManager(sessionManager());return securityManager;}/*** 处理 Web 会话* Session Manager会话管理* 即用户登录后就是一次会话在没有退出之前它的所有信息都在会话中* 会话可以是普通JavaSE环境的也可以是如Web环境的* return*/Beanpublic DefaultWebSessionManager sessionManager() {DefaultWebSessionManager defaultWebSessionManagernew DefaultWebSessionManager();// 设置session过期时间3600sLong timeout60L*1000*60;//毫秒级别//设置全局会话超时时间defaultWebSessionManager.setGlobalSessionTimeout(timeout);// 启用会话验证调度器定期检查会话的有效性defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);// 禁用URL会话ID重写 去掉shiro登录时url里的JSESSIONIDdefaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);return defaultWebSessionManager;}/*** 加密算法* return*/Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName(MD5);//采用MD5 进行加密hashedCredentialsMatcher.setHashIterations(1);//加密次数return hashedCredentialsMatcher;}/*** 记住我的配置* return*/Beanpublic RememberMeManager rememberMeManager() {Cookie cookie new SimpleCookie(rememberMe);cookie.setHttpOnly(true);//通过js脚本将无法读取到cookie信息cookie.setMaxAge(60 * 60 * 24);//cookie保存一天CookieRememberMeManager managernew CookieRememberMeManager();manager.setCookie(cookie);return manager;}/*** 缓存配置* return*/Beanpublic CacheManager cacheManager() {MemoryConstrainedCacheManager cacheManagernew MemoryConstrainedCacheManager();//使用内存缓存return cacheManager;}/*** 配置realm用于认证和授权* param hashedCredentialsMatcher* return*/Beanpublic AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {MyShiroRealm shiroRealm new MyShiroRealm();//校验密码用到的算法
// shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);return shiroRealm;}/*** 启用shiro方言这样能在页面上使用shiro标签* return*/
// Bean
// public ShiroDialect shiroDialect() {
// return new ShiroDialect();
// }/*** 开启shiro注解支持例如RequiresRoles、RequiresPermissions 等* AuthorizationAttributeSourceAdvisor 和 DefaultAdvisorAutoProxyCreator 的共同作用下注解才能起效*/Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}/*** AOP 相关的自动代理创建器* 这个才能解决权限注解不生效问题*/Beanpublic DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}}ShiroFilterMapFactory 抽出来的工具类 用于路径映射和拦截 可参考Shiro拦截机制表 里面有配置说明 package com.example.springbootshiro.config;import java.util.LinkedHashMap;/*** ClassName ShiroFilterMapFactory*/
public class ShiroFilterMapFactory {public static LinkedHashMapString, String shiroFilterMap() {//设置路径映射注意这里要用LinkedHashMap 保证有序LinkedHashMapString, String filterChainDefinitionMap new LinkedHashMap();//对所有用户认证filterChainDefinitionMap.put(/static/**, anon);filterChainDefinitionMap.put(/login, anon);//退出拦截器退出成功后重定向的到URL为“/”filterChainDefinitionMap.put(/logout, logout);//对所有页面进行认证filterChainDefinitionMap.put(/**, authc);return filterChainDefinitionMap;}
}扩展Shiro过滤机制表
过滤名称类描述anonorg.apache.shiro.web.filter.authc.AnonymousFilter匿名拦截器即不需要登录即可访问一般用于静态资源过滤示例/static/**anonauthcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter基于表单的拦截器如/**authc如果没有登录会跳到相应的登录页面登录authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilterBasic HTTP身份验证拦截器logoutorg.apache.shiro.web.filter.authc.LogoutFilter退出拦截器主要属性redirectUrl退出成功后重定向的地址/示例/logoutlogoutnoSessionCreationorg.apache.shiro.web.filter.session.NoSessionCreationFilter不创建会话拦截器调用subject.getSession(false)不会有什么问题但是如果subject.getSession(true)将抛出DisabledSessionException异常permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter权限授权拦截器验证用户是否拥有所有权限属性和roles一样示例/user/**perms[“user:create”]portorg.apache.shiro.web.filter.authz.PortFilter端口拦截器主要属性port(80)可以通过的端口示例/test port[80]如果用户访问该页面是非80将自动将请求端口改为80并重定向到该80端口其他路径/参数等都一样restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterrest风格拦截器自动根据请求方法构建权限字符串示例/usersrest[user]会自动拼出user:read,user:create,user:update,user:delete权限字符串进行权限匹配所有都得匹配isPermittedAllrolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter角色授权拦截器验证用户是否拥有所有角色示例/admin/**roles[admin]sslorg.apache.shiro.web.filter.authz.SslFilterSSL拦截器只有请求协议是https才能通过否则自动跳转会https端口443其他和port拦截器一样userorg.apache.shiro.web.filter.authc.UserFilter用户拦截器用户已经身份验证/记住我登录的都可示例/**user
7. 开始测试—后端
1. 创建一个LoginController 类
用来处理登录访问请求
package com.example.springbootshiro.controller;import com.example.springbootshiro.entity.User;
import com.example.springbootshiro.result.ResponseResult;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** ClassName LoginController*/
Controller
public class LoginController {GetMapping(/login)public String login(){return login;}GetMapping(/)public String home(){return redirect:/index;}GetMapping(/index)public String index(Model model){User user (User) SecurityUtils.getSubject().getPrincipal();model.addAttribute(user,user);return index;}PostMapping(login)ResponseBodypublic ResponseResult login(User user, Boolean rememberMe){System.out.println(user user);UsernamePasswordToken token new UsernamePasswordToken(user.getUsername(), user.getPassword());//获取Subject 对象Subject subject SecurityUtils.getSubject();try {if (rememberMe){//是否记住我token.setRememberMe(true);}subject.login(token);return ResponseResult.success(/index);} catch (UnknownAccountException e) {return ResponseResult.fail(e.getMessage());} catch (IncorrectCredentialsException e) {return ResponseResult.fail(e.getMessage());}}GetMapping(/403)public String forbid(){return 403;}
}2. 创建一个UserController 类
用于处理User类的访问请求并使用Shiro权限注解控制权限
package com.example.springbootshiro.controller;import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** ClassName UserController*/
RestController
RequestMapping(/user)
public class UserController {RequiresPermissions(user:queryAll)GetMapping(/queryAll)public String queryAll(){//只演示框架...功能不实现return 查询列表;}RequiresPermissions(user:add)GetMapping(/add)public String userAdd(){return 添加用户;}RequiresPermissions(user:delete)GetMapping(/delete)public String userDelete(){return 删除用户;}
}8. 开始测试—前端 1. 编写login.html页面
!DOCTYPE html
html langen
headmeta charsetUTF-8titleLogin Form/title!-- 确保jQuery库被加载 --script srchttps://code.jquery.com/jquery-3.3.1.min.js/script/head
body
form idloginForminput typetext idusername nameusername classtext /input typepassword idpassword namepassword classpassword /!-- 添加一个复选框来表示布尔值选项 --input typecheckbox idrememberMe namerememberMe valuetrue /label forrememberMe记住我/label
/form
div classsignininput idloginBut typebutton valueLogin /
/divscript typetext/javascript// jQuery插件定义$.fn.serializeObject function () {var o {};var a this.serializeArray();$.each(a, function () {if (o[this.name]) {if (!o[this.name].push) {o[this.name] [o[this.name]];}o[this.name].push(this.value);} else {o[this.name] this.value || ;}});return o;};$(function() {$(#loginBut).click(function() {var arr $(#loginForm).serializeObject();// 检查rememberMe复选框是否被选中if (!$(#rememberMe).is(:checked)) {// 如果未选中则显式添加rememberMe: false到arr中arr.rememberMe false;}$.ajax({url: /login,type: post,data: arr,dataType: json,success: function(data) {console.log(data,data,location)if (data.code 200) {//debugger;console.log(data,data)location.href data.message;} else {alert(data.message);}},error: function(jqXHR, textStatus, errorThrown) {// 使用jqXHR.responseText或jqXHR.responseJSON来获取错误信息if (jqXHR.responseJSON) {alert(jqXHR.responseJSON.msg || Unknown error);} else {alert(Error: textStatus);}}});});});
/script
/body
/html2. 编写index.html页面
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title首页/title
/head
body
h1我是templates 下面的 index/h1
h1番茄欢迎您/h1
!-- 使用 th:text 和 th:if 一起检查 user 是否为 null --
span th:text${user ! null ? user.username : 未登录}登录用户未登录/span
a th:href{/logout}注销/ah2权限测试/h2
a th:href{/user/queryAll}获取用户全部信息/a
a th:href{/user/add}添加用户/a
a th:href{/user/delete}删除用户/a
/body
/html3. 编写403页面
!DOCTYPE html
html langen
headmeta charsetUTF-8title403/title
/head
body
h1403权限不够/h1
a href/index首页/a
/body
/html10、测试遇到的问题
启动项目访问http://localhost:8080/,它会自动拦截页面重定向到 http://localhost:8080/login 登录成功跳转到http://localhost:8080/index
问题 登录测试用户的时候访问没有权限的链接请求时后台抛出Caused by:org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method 异常后 以为会因为我们在ShiroConfig配置类中配置了shiroFilterFactoryBean.setUnauthorizedUrl(“/403”); 从而跳到我们写的403页面 结果却并没有触发。
原因如下 只有permsrolessslrestport才是属于AuthorizationFilter 而anonauthcBasicauchcuser是AuthenticationFilter 所以unauthorizedUrl设置后页面不跳转。
解决方案 方案1:在ShiroFilterMapFactory.shiroFilterMap() 我们配置的 页面权限控制中 换掉 anon 方案2:要么配置全局拦截器 推荐这种比较好
RestControllerAdvice
public class GlobalException {ExceptionHandler(value AuthorizationException.class)public String handleAuthorizationException() {return 全局处理器的 403;}五、SpringBoot 集成 Shiro shiro-spring-boot-web-starter方式
1. 添加 maven 依赖 !--增加Shiro的配置--dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring-boot-web-starter/artifactIdversion1.4.0/version/dependency!--Thymeleaf模板引擎--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependency!--在Thymeleaf中使用shiro标签--dependencygroupIdcom.github.theborakompanioni/groupIdartifactIdthymeleaf-extras-shiro/artifactIdversion2.0.0/version/dependency2. 添加Shiro的配置 和 “SpringBoot集成Shiro权限框架shiro-spring 方式”相比。 去掉ShiroConfig配置类中关于ShiroFilterFactoryBean的初始化配置移到了application.properties文件中。 properties 格式
#开启Shiro配置默认为true
shiro.enabledtrue
#开启ShiroWeb配置默认为true
shiro.web.enabledtrue
#登录地址
shiro.loginUrl/login
#登录成功地址
shiro.successUrl/index
#未获授权默认跳转地址
shiro.unauthorizedUrl/unauthorized
#是否允许将sessionId放到Url地址拦中。默认为true。默认为true
shiro.sessionManager.sessionIdUrlRewritingEnabledtrue
#是否允许将sessionId放到cookie中默认为true
shiro.sessionManager.sessionIdCookieEnabledtrue3. 添加 ShiroConfig 配置类 和 “SpringBoot集成Shiro权限框架shiro-spring 方式”相比。 去掉ShiroConfig配置类中关于ShiroFilterFactoryBean的实例化方法改为ShiroFilterChainDefinition。 Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition definition new DefaultShiroFilterChainDefinition();//页面权限控制definition.addPathDefinitions(ShiroFilterMapFactory.shiroFilterMap());return definition;
}其他的配置和操作与 “SpringBoot集成Shiro权限框架shiro-spring 方式” 一样不再赘述。
参考文章 【1】SpringBootShiro实现权限管理 【2】Spring Boot整合Shiro两种方式实战总结含源码 【3】Springboot集成Shiro前后端分离 【4】SpringBoot2.0 整合 Shiro 框架实现用户权限管理 【5】springboot整合shiro入门实现认证和授权功能非常详细 【6】SpringBoot整合ShiroJwt实现登录认证和授权 【7】十二整合 Shiro 框架实现用户权限管理-爱代码爱编程 【8】超详细 Spring Boot 整合 Shiro 教程