Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
者:社會主義接班人cnblogs.com/5ishare/p/10461073.html
用戶權限管理一般是對用戶頁面、按鈕的訪問權限管理。Shiro框架是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理,對于Shiro的介紹這里就不多說。本篇博客主要是了解Shiro的基礎使用方法,在權限管理系統中集成Shiro實現登錄、url和頁面按鈕的訪問控制。
一、引入依賴
使用SpringBoot集成Shiro時,在pom.xml中可以引入shiro-spring-boot-web-starter。由于使用的是thymeleaf框架,thymeleaf與Shiro結合需要 引入thymeleaf-extras-shiro。
二、增加Shiro配置
有哪些url是需要攔截的,哪些是不需要攔截的,登錄頁面、登錄成功頁面的url、自定義的Realm等這些信息需要設置到Shiro中,所以創建Configuration文件ShiroConfig。
ShiroDialect這個bean對象是在thymeleaf與Shiro結合,前端html訪問Shiro時使用。
三、自定義Realm
在自定義的Realm中繼承了AuthorizingRealm抽象類,重寫了兩個方法:doGetAuthorizationInfo和doGetAuthenticationInfo。doGetAuthorizationInfo主要是用來處理權限配置,doGetAuthenticationInfo主要處理身份認證。
這里在doGetAuthorizationInfo中,將role表的id和permission表的code分別設置到SimpleAuthorizationInfo對象中的role和permission中。
還有一個地方需要注意:@Component("authorizer"),剛開始我沒設置,但報錯提示需要一個authorizer的bean,查看AuthorizingRealm可以發現它implements了Authorizer,所以在自定義的realm上添加@Component("authorizer")就可以了。
四、登錄認證
1.登錄頁面
這里做了一個非常丑的登錄頁面,主要是自己懶,不想在網上復制粘貼找登錄頁面了。
2.處理登錄請求
在LoginController中通過登錄名、密碼獲取到token實現登錄。
五、Controller層訪問控制
1.首先來數據庫的數據,兩張圖是用戶角色、和角色權限的數據。
2.設置權限
這里在用戶頁面點擊編輯按鈕時設置需要有id=002的角色,在點擊選擇角色按鈕時需要有code=002的權限。
@RequestMapping(value = "/edit",method = RequestMethod.GET) @RequiresRoles("002")//權限管理; public String editGet(Model model,@RequestParam(value="id") String id) { model.addAttribute("id", id); return "/user/edit"; }
@RequestMapping(value = "/selrole",method = RequestMethod.GET) @RequiresPermissions("002")//權限管理; public String selctRole(Model model,@RequestParam("id") String id,@RequestParam("type") Integer type) { model.addAttribute("id",id); model.addAttribute("type", type); return "/user/selrole"; }
當使用用戶001登錄時,點擊編輯,彈出框如下,提示沒有002的角色
點擊選擇角色按鈕時提示沒有002的權限。
當使用用戶002登錄時,點擊編輯按鈕,顯示正常,點擊選擇角色也是提示沒002的權限,因為權限只有001。
六、前端頁面層訪問控制
有時為了不想像上面那樣彈出錯誤頁面,需要在按鈕顯示上進行不可見,這樣用戶也不會點擊到。前面已經引入了依賴并配置了bean,這里測試下在html中使用shiro。
1.首先設置html標簽引入shiro
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
2.控制按鈕可見
這里使用shiro:hasAnyRoles="002,003"判斷用戶角色是否是002或003,是則顯示不是則不顯示。
<div class="layui-inline"> <a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-normal newsAdd_btn" onclick="addUser('')">添加用戶</a> </div> <div class="layui-inline"> <a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-danger batchDel" onclick="getDatas();">批量刪除</a> </div>
當001用戶登錄時,添加用戶、批量刪除按鈕都不顯示,只顯示查詢按鈕。
當002用戶登錄時,添加用戶、批量刪除按鈕都顯示
七、小結
這里只是實現了Shiro的簡單的功能,Shiro還有很多很強大的功能,比如session管理等,而且目前權限管理模塊還有很多需要優化的功能,左側導航欄的動態加載和權限控制、Shiro與Redis結合實現session共享、Shiro與Cas結合實現單點登錄等。后續可以把項目做為開源項目,慢慢完善集成更多模塊例如:Swagger2、Redis、Druid、RabbitMQ等供初學者參考。
實際項目中,經常需要用到角色權限區分,以此來為不同的角色賦予不同的權利,分配不同的任務。比如,普通用戶只能瀏覽;會員可以瀏覽和評論;超級會員可以瀏覽、評論和看視頻課等;實際應用場景很多。毫不夸張的說,幾乎每個完整的項目都會涉及到權限管理。
因此,這篇文章,阿淼就帶大家將 shiro 權限框架整合到 SpringBoot 中,以達到快速的實現權限管理的功能。
在 Spring Boot 中做權限管理,一般來說,主流的方案是 Spring Security ,但是由于 Spring Security 過于龐大和復雜,只要能滿足業務需要,大多數公司還是會選擇 Apache Shiro 來使用。
一般來說,Spring Security 和 Shiro 的區別如下:
Spring SecurityApache Shiro重量級的安全管理框架輕量級的安全管理框架概念復雜,配置繁瑣概念簡單、配置簡單功能強大功能簡單
這篇文章會首先帶大家了解 Apache Shiro ,然后再給出使用案例 Demo。
照例又去官網扒了扒介紹:
Apache Shiro? is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications. Apache Shiro?是一個強大且易用的Java安全框架,能夠用于身份驗證、授權、加密和會話管理。Shiro擁有易于理解的API,您可以快速、輕松地獲得任何應用程序——從最小的移動應用程序到最大的網絡和企業應用程序。
簡而言之,Apache Shiro 是一個強大靈活的開源安全框架,可以完全處理身份驗證、授權、加密和會話管理。
對此,官方給出了詳細的解釋:shiro.apache.org/
自2003年以來,框架環境發生了很大變化,因此今天仍然有充分的理由使用Shiro。實際上有很多原因。Apache Shiro是:
Apache Shiro 是一個全面的、蘊含豐富功能的安全框架。
下圖為描述 Shiro 功能的框架圖:
如圖所示,功能包括:
并且 Shiro 還有通過增加其他的功能來支持和加強這些不同應用環境下安全領域的關注點。
特別是對以下的功能支持:
注意: Shiro 不會去維護用戶、維護權限,這些需要我們自己去設計/提供,然后通過相應的接口注入給 Shiro
為方便我們初始化項目,Spring Boot給我們提供一個項目模板生成網站。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
復制代碼
web 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
復制代碼
shiro-spring 包就是此篇文章的核心
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
shiro 注解會用到 aop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
數據庫相關包使用的是mybatisplus
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
復制代碼
建表語句在項目中有,項目地址: github.com/mmzsblog/mm…
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
// .getSubject()).getServletRequest();//這個可以用來獲取在登錄的時候提交的其他額外的參數信息
String username = (String) principals.getPrimaryPrincipal();
// 受理權限
// 角色
Set<String> roles = new HashSet<String>();
Role role = roleService.getRoleByUserName(username);
System.out.println(role.getRoleName());
roles.add(role.getRoleName());
authorizationInfo.setRoles(roles);
// 權限
Set<String> permissions = new HashSet<String>();
List<Permission> querypermissions = permissionService.getPermissionsByRoleId(role.getId());
for (Permission permission : querypermissions) {
permissions.add(permission.getPermissionName());
}
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
String loginName = (String) authcToken.getPrincipal();
// 獲取用戶密碼
User user = userService.getOne(new QueryWrapper<User>().eq("username", loginName));
if (user == null) {
// 沒找到帳號
throw new UnknownAccountException();
}
String password = new String((char[]) authcToken.getCredentials());
String inpass = (new Md5Hash(password, user.getUsername())).toString();
if (!user.getPassword().equals(inpass)) {
throw new IncorrectCredentialsException();
}
// 交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(loginName, user.getPassword(),
ByteSource.Util.bytes(loginName), getName());
return authenticationInfo;
}
}
復制代碼
@Configuration
public class ShiroConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
/**
* Shiro的Web過濾器Factory 命名:shiroFilter
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,這個屬性是必須的
shiroFilterFactoryBean.setSecurityManager(securityManager);
//需要權限的請求,如果沒有登錄則會跳轉到這里設置的url
shiroFilterFactoryBean.setLoginUrl("/login.html");
//設置登錄成功跳轉url,一般在登錄成功后自己代碼設置跳轉url,此處基本沒用
shiroFilterFactoryBean.setSuccessUrl("/main.html");
//設置無權限跳轉界面,此處一般不生效,一般自定義異常
shiroFilterFactoryBean.setUnauthorizedUrl("/error.html");
Map<String, Filter> filterMap = new LinkedHashMap<>();
// filterMap.put("authc", new AjaxPermissionsAuthorizationFilter());
shiroFilterFactoryBean.setFilters(filterMap);
/*
* 定義shiro過濾鏈 Map結構
* Map中key(xml中是指value值)的第一個'/'代表的路徑是相對于HttpServletRequest.getContextPath()的值來的
* anon:它對應的過濾器里面是空的,什么都沒做,這里.do和.jsp后面的*表示參數,比方說login.jsp?main這種
* authc:該過濾器下的頁面必須驗證后才能訪問,它是Shiro內置的一個攔截器org.apache.shiro.web.filter.authc.
* FormAuthenticationFilter
*/
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
/*
* 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊; authc:所有url都必須認證通過才可以訪問;
* anon:所有url都都可以匿名訪問
*/
filterChainDefinitionMap.put("/login.html", "authc");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 權限管理
*/
@Bean
public SecurityManager securityManager() {
logger.info("=======================shiro=======================");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(MyShiroRealm());
// securityManager.setRememberMeManager(rememberMeManager);
return securityManager;
}
/**
* Shiro Realm 繼承自AuthorizingRealm的自定義Realm,即指定Shiro驗證用戶登錄的類為自定義的
*/
@Bean
public MyShiroRealm MyShiroRealm() {
MyShiroRealm userRealm = new MyShiroRealm();
userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return userRealm;
}
/**
* 憑證匹配器 密碼驗證
*/
@Bean(name = "credentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 散列算法:這里使用MD5算法;
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 散列的次數,比如散列兩次,相當于 md5(md5(""));
hashedCredentialsMatcher.setHashIterations(1);
// storedCredentialsHexEncoded默認是true,此時用的是密碼加密用的是Hex編碼;false時用Base64編碼
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
/**
* 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,并在必要時進行安全邏輯驗證
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
}
復制代碼
@RestController
public class UserController {
@PostMapping("login")
public String name(String username, String password) {
String result = "已登錄";
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
if (!currentUser.isAuthenticated()) {
try {
currentUser.login(token);// 會觸發com.shiro.config.MyShiroRealm的doGetAuthenticationInfo方法
result = "登錄成功";
} catch (UnknownAccountException e) {
result = "用戶名錯誤";
} catch (IncorrectCredentialsException e) {
result = "密碼錯誤";
}
}
return result;
}
@GetMapping("logout")
public void logout() {
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
}
@RequiresPermissions("role:update")
@GetMapping("/role")
public String name() {
return "hello";
}
@RequiresPermissions("user:select")
@GetMapping("/role2")
public String permission() {
return "hello sel";
}
}
復制代碼
數據庫賬號(密碼經過md5加鹽加密)
無權限時自定義了一個異常。所以,權限測試的時候沒有權限就會提示配置的提示語 “沒有權限”。
@ControllerAdvice
public class ShiroException {
@ExceptionHandler(value = UnauthorizedException.class)
@ResponseBody
public String name() {
return "沒有權限";
}
}
復制代碼
權限設置可在shiro配置類中shiro過濾鏈設置,也可用注解方式設置,本文使用注解方式。
shiro 的 session 和 cache 管理可以自定義,本文用的是默認的,推薦自定義,方便管理。
hiro 1.7.0 發布,此版本包括自 1.6.0 版本以來解決的 7 個問題,現在可以從 maven 中央倉庫下載使用。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.7.0</version>
<type>pom</type>
</dependency>
值得注意的是:
關于 shiro 1.7 changelog 可以參考 issues.apache.org/jira
有關 Shiro 的更多信息,請閱讀文檔 http://shiro.apache.org/documentation.html。
Apache Shiro 是一個強大且易用的 Java 安全框架,執行身份驗證、授權、密碼和會話管理。使用 Shiro 的易于理解的 API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。