shiro的使用,Spring Boot教程(十六):Spring Boot集成shiro

 2023-10-06 阅读 29 评论 0

摘要:Apache Shiro?是一個功能強大且易于使用的Java安全框架,可執行身份驗證,授權,加密和會話管理。借助Shiro易于理解的API,您可以快速輕松地保護任何應用程序 - 從最小的移動應用程序到最大的Web和企業應用程序。更多內容請查看官網 一、項目準備 為

Apache Shiro?是一個功能強大且易于使用的Java安全框架,可執行身份驗證,授權,加密和會話管理。借助Shiro易于理解的API,您可以快速輕松地保護任何應用程序 - 從最小的移動應用程序到最大的Web和企業應用程序。更多內容請查看官網

一、項目準備

為了方便,這里直接使用Spring Boot教程(十一):Spring Boot集成fastjson章節的源碼。

二、添加依賴

<!-- shiro -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.4.0</version>
</dependency>

三、創建用戶資源權限相關的表

sql文件放在db目錄下。


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (`id` bigint(20) NOT NULL,`parent_id` bigint(20) DEFAULT NULL,`res_name` varchar(50) DEFAULT NULL,`res_type` varchar(10) DEFAULT NULL,`permission` varchar(20) DEFAULT NULL,`url` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_permission
-- ----------------------------
BEGIN;
INSERT INTO `sys_permission` VALUES (1, NULL, '系統管理', 'menu', 'system', NULL);
INSERT INTO `sys_permission` VALUES (2, 1, '用戶管理', 'menu', 'systemUserList', '/user/userList');
INSERT INTO `sys_permission` VALUES (3, 1, '角色管理', 'menu', 'systemRole', '/roles');
INSERT INTO `sys_permission` VALUES (4, NULL, '一級菜單', 'menu', 'menu', NULL);
INSERT INTO `sys_permission` VALUES (5, 4, '二級菜單1', 'menu', 'menuXxx', '/xxx');
INSERT INTO `sys_permission` VALUES (6, 4, '二級菜單2', 'menu', 'menuYyy', '/yyy');
INSERT INTO `sys_permission` VALUES (7, 2, '用戶添加', 'button', 'systemUserAdd', NULL);
COMMIT;-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (`role_id` bigint(20) NOT NULL,`role_name` varchar(30) DEFAULT NULL,PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_role` VALUES (1, '用戶管理員');
INSERT INTO `sys_role` VALUES (2, '普通用戶');
COMMIT;-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (`role_id` bigint(20) NOT NULL,`permission_id` bigint(20) NOT NULL,PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
BEGIN;
INSERT INTO `sys_role_permission` VALUES (1, 1);
INSERT INTO `sys_role_permission` VALUES (1, 2);
INSERT INTO `sys_role_permission` VALUES (1, 3);
INSERT INTO `sys_role_permission` VALUES (1, 4);
INSERT INTO `sys_role_permission` VALUES (1, 5);
INSERT INTO `sys_role_permission` VALUES (1, 6);
INSERT INTO `sys_role_permission` VALUES (1, 7);
INSERT INTO `sys_role_permission` VALUES (2, 1);
INSERT INTO `sys_role_permission` VALUES (2, 2);
COMMIT;-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`user_id` bigint(20) NOT NULL,`user_name` varchar(50) DEFAULT NULL,`full_name` varchar(20) DEFAULT NULL,`password` varchar(32) DEFAULT NULL,`salt` varchar(20) DEFAULT NULL,PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` VALUES (1, 'zhangsan', '張三', '86fb1b048301461bdc71d021d2af3f97', '1');
INSERT INTO `sys_user` VALUES (2, 'lisi', '李四', 'c9351e5cf153923f052ebe1462cca93a', '2');
INSERT INTO `sys_user` VALUES (3, 'wangwu', '王五', '92262648696eae1b0a321cbd2b238bf2', '3');
INSERT INTO `sys_user` VALUES (4, 'user1', '用戶1', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (5, 'user2', '用戶2', '86fb1b048301461bdc71d021d2af3f97', '5');
INSERT INTO `sys_user` VALUES (6, 'user3', '用戶3', '86fb1b048301461bdc71d021d2af3f97', '5');
INSERT INTO `sys_user` VALUES (7, 'user4', '用戶4', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (8, 'user5', '用戶5', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (9, 'user6', '用戶6', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (10, 'user7', '用戶7', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (11, 'user8', '用戶8', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (12, 'user9', '用戶9', '86fb1b048301461bdc71d021d2af3f97', '4');
INSERT INTO `sys_user` VALUES (13, 'user10', '用戶10', '86fb1b048301461bdc71d021d2af3f97', '4');
COMMIT;-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (`user_id` bigint(20) NOT NULL,`role_id` bigint(20) NOT NULL,PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2);
COMMIT;SET FOREIGN_KEY_CHECKS = 1;

四、創建或反向生成實體

生成SysPermissionSysUser等實體,并在實體的id字段上添加@Id注解(Mapper4使用,不了解的可以查看之前的文章),為了節省篇幅,這里不貼出實體內容了,具體內容參考文章底部配套源碼。

五、創建service、dao等

這些內容和shiro沒有關系,普通的查詢,只是用來提供數據,所以不詳細說了,參考源碼。

shiro的使用,根據用戶id查詢權限字符串(給shiro提供授權數據信息):

List<String> SysPermissionService.selectPermissionByUserId(long userId)

根據用戶名查詢用戶信息(給shiro提供登錄認證信息):

SysUser SysUserService findByUserName(String userName)

六、創建自定義Realm

創建一個UserRealm類,并繼承AuthorizingRealm,用來給shiro注入認證信息和授權信息。

package com.songguoliang.springboot.shiro;import com.songguoliang.springboot.entity.SysUser;
import com.songguoliang.springboot.service.SysPermissionService;
import com.songguoliang.springboot.service.SysUserService;
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.authc.UsernamePasswordToken;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/*** @Description* @Author sgl* @Date 2018-06-11 17:07*/
public class UserRealm extends AuthorizingRealm {private static final Logger LOGGER = LoggerFactory.getLogger(UserRealm.class);@Autowiredprivate SysUserService sysUserService;@Autowiredprivate SysPermissionService sysPermissionService;/*** 授權** @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();List<String> sysPermissions = sysPermissionService.selectPermissionByUserId(sysUser.getUserId());SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addStringPermissions(sysPermissions);LOGGER.info("doGetAuthorizationInfo");return info;}/*** 認證** @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;SysUser sysUser = sysUserService.findByUserName(token.getUsername());if (sysUser == null) {return null;}LOGGER.info("doGetAuthenticationInfo");return new SimpleAuthenticationInfo(sysUser, sysUser.getPassword().toCharArray(), ByteSource.Util.bytes(sysUser.getSalt()), getName());}
}

七、創建ShiroConfig配置類

package com.songguoliang.springboot.shiro;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;/*** @Description* @Author sgl* @Date 2018-06-11 17:23*/
@Configuration
public class ShiroConfig {/*** 憑證匹配器** @return*/@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//md5加密1次hashedCredentialsMatcher.setHashAlgorithmName("md5");hashedCredentialsMatcher.setHashIterations(1);return hashedCredentialsMatcher;}/*** 自定義realm** @return*/@Beanpublic UserRealm userRealm() {UserRealm userRealm = new UserRealm();userRealm.setCredentialsMatcher(hashedCredentialsMatcher());return userRealm;}/*** 安全管理器* 注:使用shiro-spring-boot-starter 1.4時,返回類型是SecurityManager會報錯,直接引用shiro-spring則不報錯** @return*/@Beanpublic DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(userRealm());return securityManager;}/*** 設置過濾規則** @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setSuccessUrl("/");shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");//注意此處使用的是LinkedHashMap,是有順序的,shiro會按從上到下的順序匹配驗證,匹配了就不再繼續驗證//所以上面的url要苛刻,寬松的url要放在下面,尤其是"/**"要放到最下面,如果放前面的話其后的驗證規則就沒作用了。Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();filterChainDefinitionMap.put("/static/**", "anon");filterChainDefinitionMap.put("/login", "anon");filterChainDefinitionMap.put("/captcha.jpg", "anon");filterChainDefinitionMap.put("/favicon.ico", "anon");filterChainDefinitionMap.put("/**", "authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}}

八、創建登錄Action

登錄時,我們只需要將用戶輸入的用戶名和密碼交給shiro即可,用戶名和密碼是否正確完全交給shiro來驗證。

package com.songguoliang.springboot.controller;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
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.RequestMapping;/*** @Description* @Author sgl* @Date 2018-06-11 17:51*/
@Controller
public class LoginController {/*** get請求,登錄頁面跳轉* @return*/@GetMapping("/login")public String login() {//如果已經認證通過,直接跳轉到首頁if (SecurityUtils.getSubject().isAuthenticated()) {return "redirect:/index";}return "login";}/*** post表單提交,登錄* @param username* @param password* @param model* @return*/@PostMapping("/login")public Object login(String username, String password, Model model) {Subject user = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {//shiro幫我們匹配密碼什么的,我們只需要把東西傳給它,它會根據我們在UserRealm里認證方法設置的來驗證user.login(token);return "redirect:/index";} catch (UnknownAccountException e) {//賬號不存在和下面密碼錯誤一般都合并為一個賬號或密碼錯誤,這樣可以增加暴力破解難度model.addAttribute("message", "賬號不存在!");} catch (DisabledAccountException e) {model.addAttribute("message", "賬號未啟用!");} catch (IncorrectCredentialsException e) {model.addAttribute("message", "密碼錯誤!");} catch (Throwable e) {model.addAttribute("message", "未知錯誤!");}return "login";}/*** 退出* @return*/@RequestMapping("/logout")public String logout() {SecurityUtils.getSubject().logout();return "login";}
}

九、修改IndexController

package com.songguoliang.springboot.controller;import com.songguoliang.springboot.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;/*** @Description* @Author sgl* @Date 2018-05-08 10:47*/
@Controller
public class IndexController {/*** 首頁,并將登錄用戶的全名返回前臺* @param model* @return*/@RequestMapping(value = {"/", "/index"})public String index(Model model) {SysUser sysUser = (SysUser) SecurityUtils.getSubject().getPrincipal();model.addAttribute("userName", sysUser.getFullName());return "index";}
}

十、修改TestController

在TestController里添加以下兩個方法,用來驗證shiro權限注解,

/*** 沒有加shiro權限注解* @return*/
@RequestMapping("/test1")
public String test1(){return "test1";
}/*** 添加了shiro權限注解,需要用戶有"systemUserAdd"權限* @return*/
@RequestMapping("/test2")
@RequiresPermissions("systemUserAdd")
public String test2(){return "test2";
}

十一、添加頁面

由于代碼里已經集成了jsp支持,所以這里延續用jsp,你也可以用其他的方法。

shiro怎么用,在webapp/WEB-INF/jsp下創建login.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/><title>登錄</title>
</head>
<body>
<h1>${message}</h1>
<form method="post" action="/login">用戶名:<input name="username"><br>密碼:<input name="password"><br><input type="submit" value="登錄">
</form></body>
</html>

十二、通過springboot的maven插件啟動服務進行測試

1、輸入http://localhost:8080/頁面不再直接顯示首頁,而是跳轉到了登錄頁面:

這里寫圖片描述

2、用戶名、密碼隨意輸入,比如test/test,點擊登錄,頁面顯示賬號不存在。

這里寫圖片描述

3、用戶名輸入zhangsan,密碼隨意輸入,提示密碼錯誤

為什么要用shiro框架、這里寫圖片描述

4、輸入zhangsan/zhangsan,登錄驗證通過,直接跳轉到首頁

這里寫圖片描述

下面繼續進行測試權限注解:

5、當前張三已經登錄,訪問http://localhost:8080/test1,顯示test1

這里寫圖片描述

Springboot注解?6、然后再訪問http://localhost:8080/test2,顯示test2

這里寫圖片描述

7、退出張三用戶的登錄,訪問http://localhost:8080/logout,此時再訪問http://localhost:8080/test1http://localhost:8080/test2都會跳轉到登錄頁面。

8、再用lisi/lisi登錄,首頁如下:

這里寫圖片描述

9、然后訪問http://localhost:8080/test1,同樣可以顯示test1

Spring boot,這里寫圖片描述

10、然后訪問http://localhost:8080/test2,由于lisi沒有systemUserAdd權限,所以他不能訪問,顯示如下(這是默認的錯誤處理頁面,實際項目中一般都會自定義錯誤處理):

這里寫圖片描述




?

源碼:
github
碼云

--------------------- 作者:gnail_oug 來源:CSDN 原文:https://blog.csdn.net/gnail_oug/article/details/80662553?utm_source=copy 版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/120801.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息