shiro
一、shiro是什么?
Shiro是Apache下的一個開源項目改基。shiro屬于輕量級框架霉翔,相對于SpringSecurity簡單的多,也沒有SpringSecurity那么復(fù)雜 柒凉。
二族阅、主要功能
shiro主要有三大功能模塊:
1. Subject:主體,一般指用戶膝捞。
2. SecurityManager:安全管理器坦刀,管理所有Subject,可以配合內(nèi)部安全組件绑警。(類似于SpringMVC中的DispatcherServlet)
3. Realms:用于進行權(quán)限信息的驗證端礼,一般需要自己實現(xiàn)允跑。
細分功能
1. Authentication:身份認證/登錄(賬號密碼驗證)欲鹏。
2. Authorization:授權(quán)挂洛,即角色或者權(quán)限驗證吏廉。
3. Session Manager:會話管理蕊温,用戶登錄后的session相關(guān)管理跛蛋。
4. Cryptography:加密蝌麸,密碼加密等。
5. Web Support:Web支持咕村,集成Web環(huán)境场钉。
6. Caching:緩存,用戶信息懈涛、角色逛万、權(quán)限等緩存到如redis等緩存中。
7. Concurrency:多線程并發(fā)驗證批钠,在一個線程中開啟另一個線程宇植,可以把權(quán)限自動傳播過去。
8. Testing:測試支持埋心;
9. Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問指郁。
10. Remember Me:記住我,登錄后拷呆,下次再來的話不用登錄了
三闲坎、具體實現(xiàn)
1、項目目錄
pom.xml依賴文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>shiro_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring.shiro.version>1.6.0</spring.shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${spring.shiro.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--頁面模板依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--熱部署依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Permissions權(quán)限實體類
package org.example.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Permissions {
private String id;
private String permissionsName;
}
Role角色實體
package org.example.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Set;
@Data
@AllArgsConstructor
public class Role {
private String id;
private String roleName;
/**
* 角色對應(yīng)權(quán)限集合
*/
private Set<Permissions> permissions;
}
User實體
package org.example.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Set;
@Data
@AllArgsConstructor
public class User {
private String id;
private String userName;
private String password;
private Set<Role> roles;
}
業(yè)務(wù)層茬斧,模擬數(shù)據(jù)庫
package org.example.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.example.bean.Permissions;
import org.example.bean.Role;
import org.example.bean.User;
import org.example.service.LoginService;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
@Override
public User getUserByName(String getMapByName) {
return getMapByName(getMapByName);
}
/**
* 模擬數(shù)據(jù)庫查詢
*
* @param userName 用戶名
* @return User
*/
private User getMapByName(String userName) {
Permissions permissions1 = new Permissions("1", "query");
Permissions permissions2 = new Permissions("2", "add");
Set<Permissions> permissionsSet = new HashSet<>(2);
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
Role role = new Role("1", "admin", permissionsSet);
Set<Role> roleSet = new HashSet<>();
roleSet.add(role);
User user = new User("1", "wsl", "123456", roleSet);
Map<String, User> map = new HashMap<>(1);
map.put(user.getUserName(), user);
Set<Permissions> permissionsSet1 = new HashSet<>(2);
permissionsSet1.add(permissions1);
Role role1 = new Role("2", "user", permissionsSet1);
Set<Role> roleSet1 = new HashSet<>();
roleSet1.add(role1);
User user1 = new User("2", "zhangsan", "123456", roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
}
CustomRealm用戶權(quán)限配置類
package org.example.shiro;
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.subject.PrincipalCollection;
import org.example.bean.User;
import org.example.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
/**
* 權(quán)限控制以及權(quán)限認證
*
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
/**
* 權(quán)限配置類
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取當(dāng)前登錄用戶名
String userName = principalCollection.getPrimaryPrincipal().toString();
User user = loginService.getUserByName(userName);
//添加角色和權(quán)限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
user.getRoles().forEach(role -> {
simpleAuthorizationInfo.addRole(role.getRoleName());
role.getPermissions().forEach(permission -> simpleAuthorizationInfo.addStringPermission(permission.getPermissionsName()));
});
return simpleAuthorizationInfo;
}
/**
* 權(quán)限認證類
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
Object username = authenticationToken.getPrincipal();
if (StringUtils.isEmpty(username)) {
return null;
}
//獲取用戶信息
String name = username.toString();
User user = loginService.getUserByName(name);
if (user == null) {
//這里返回后會報出對應(yīng)異常
return null;
} else {
//這里驗證authenticationToken和simpleAuthenticationInfo的信息
return new SimpleAuthenticationInfo(name, user.getPassword(), getName());
}
}
}
shiro配置類
package org.example.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.example.shiro.CustomRealm;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* shiro具體配置類
*/
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
/**
* 將自己的驗證方式加入容器
*/
@Bean
public CustomRealm myShiroRealm() {
return new CustomRealm();
}
/**
* 權(quán)限管理腰懂,配置主要是Realm的管理認證
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* Filter工廠,設(shè)置對應(yīng)的過濾條件和跳轉(zhuǎn)條件
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
//登出
map.put("/logout", "logout");
//對所有用戶認證
map.put("/**", "authc");
//登錄
shiroFilterFactoryBean.setLoginUrl("/login");
//首頁
shiroFilterFactoryBean.setSuccessUrl("/index");
//錯誤頁面项秉,認證不通過跳轉(zhuǎn)
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
ExceptionHandler統(tǒng)一異常處理類
package org.example.handler;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
log.error("沒有通過權(quán)限驗證绣溜!", e);
return "沒有通過權(quán)限驗證!";
}
}
controller
package org.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.example.bean.User;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class UserController {
@GetMapping("/login")
public String login(User user) {
if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
return "請輸入用戶名和密碼伙狐!";
}
//用戶認證信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
user.getPassword()
);
try {
//進行驗證涮毫,這里可以捕獲異常,然后返回對應(yīng)信息
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
log.error("用戶名不存在贷屎!", e);
return "用戶名不存在罢防!";
} catch (AuthenticationException e) {
log.error("賬號或密碼錯誤!", e);
return "賬號或密碼錯誤唉侄!";
} catch (AuthorizationException e) {
log.error("沒有權(quán)限咒吐!", e);
return "沒有權(quán)限";
}
return "login success";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("query")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("add")
@GetMapping("/add")
public String add() {
return "add success!";
}
}