在springBoot框架下我們使用shiro做權限登錄驗證,首先要建好五張表:
1:user表
CREATE TABLE `user` (
`id` int(16) NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
`basic_info_id` int(11) NOT NULL COMMENT '企業外鍵',
`user_account` varchar(32) NOT NULL DEFAULT '' COMMENT '用戶名',
`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密碼 MD5加密',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='user';
2:role表
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(20) DEFAULT NULL COMMENT '角色名稱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='角色表';
3:user_role表
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) NOT NULL COMMENT '角色id',
`user_id` int(11) NOT NULL COMMENT '用戶id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
4:permissions表
CREATE TABLE `permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`permissions_no` varchar(20) NOT NULL COMMENT '權限字符串',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='權限表';
5:role_permissions表
CREATE TABLE `role_permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) NOT NULL COMMENT '角色id',
`permissions_id` int(11) NOT NULL COMMENT '權限id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='角色權限表';
至此表創建完畢,需要五張表
以下是java代碼
1:導入shiro的jar,以下是maven座標
<!--shiro權限驗證-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<!--shiro使用ehcache緩存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.5</version>
</dependency>
2:我們配置一下shiro配置類
package com.guoheng.safe.config;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.LinkedHashMap;
import java.util.Properties;
/**
* @author: 範保林
* @date: 2019/6/14
* @description: Shiro配置
*/
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 處理攔截資源文件問題。
* 注意:初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
* Web應用中,Shiro可控制的Web請求必須經過Shiro主過濾器的攔截
* @param securityManager
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必須設置 SecurityManager,Shiro的核心安全接口
shiroFilterFactoryBean.setSecurityManager(securityManager);
//這裏的/login是後臺的接口名,非頁面,如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
//這裏的/index是後臺的接口名,非頁面,登錄成功後要跳轉的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授權界面,該配置無效,並不會進行頁面跳轉(需要額外配置SimpleMappingExceptionResolver)
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
//自定義攔截器限制併發人數,參考博客:
//LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
//限制同一帳號同時在線的個數
//filtersMap.put("kickout", kickoutSessionControlFilter());
//shiroFilterFactoryBean.setFilters(filtersMap);
// 配置訪問權限 必須是LinkedHashMap,因爲它必須保證有序
// 過濾鏈定義,從上向下順序執行,一般將 /**放在最爲下邊 一定要注意順序,否則就不好使了
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//配置不登錄可以訪問的資源,anon 表示資源都可以匿名訪問
filterChainDefinitionMap.put("/njk/userLogin", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
// 所有接口都可以匿名訪問
filterChainDefinitionMap.put("/*/**","anon");
//logout是shiro提供的過濾器
filterChainDefinitionMap.put("/logout", "logout");
//此時訪問/gh/alarm/area/page需要root權限,在自定義Realm中爲用戶授權。
// filterChainDefinitionMap.put("/gh/alarm/area/page", "perms[\"user:root\"]");
//其他資源都需要認證 authc 表示需要認證才能進行訪問
filterChainDefinitionMap.put("/**", "authc");
//user表示配置記住我或認證通過可以訪問的地址
//filterChainDefinitionMap.put("/**", "user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* DefaultAdvisorAutoProxyCreator就是通過AOP的方式對貼了
* @RequiredPermission的類進行增強,生成對應的代理類對象.
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 開啓aop註解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 配置核心安全事務管理器
* @param shiroRealm
* @return
*/
@Bean(name="securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//設置自定義realm.
securityManager.setRealm(shiroRealm);
//配置記住我 參考博客:
securityManager.setRememberMeManager(rememberMeManager());
//配置 ehcache緩存管理器 參考博客:
// securityManager.setCacheManager(ehCacheManager());
//配置自定義session管理,使用redis 參考博客:
// securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 配置Shiro生命週期處理器
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 身份認證realm; (這個需要自己寫,賬號密碼校驗;權限等)
* @return
*/
@Bean
public ShiroRealm shiroRealm(){
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCachingEnabled(false);
//啓用身份驗證緩存,即緩存AuthenticationInfo信息,默認false
shiroRealm.setAuthenticationCachingEnabled(true);
//緩存AuthenticationInfo信息的緩存名稱 在ehcache-shiro.xml中有對應緩存的配置
shiroRealm.setAuthenticationCacheName("authenticationCache");
//啓用授權緩存,即緩存AuthorizationInfo信息,默認false
shiroRealm.setAuthorizationCachingEnabled(false);
//緩存AuthorizationInfo信息的緩存名稱 在ehcache-shiro.xml中有對應緩存的配置
shiroRealm.setAuthorizationCacheName("authorizationCache");
return shiroRealm;
}
/**
* 必須(thymeleaf頁面使用shiro標籤控制按鈕是否顯示)
* 未引入thymeleaf包,Caused by: java.lang.ClassNotFoundException: org.thymeleaf.dialect.AbstractProcessorDialect
* @return
*/
/* @Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}*/
/**
* 解決: 無權限頁面不跳轉 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized") 無效
* shiro的源代碼ShiroFilterFactoryBean.Java定義的filter必須滿足filter instanceof AuthorizationFilter,
* 只有perms,roles,ssl,rest,port纔是屬於AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,
* 所以unauthorizedUrl設置後頁面不跳轉 Shiro註解模式下,登錄失敗與沒有權限都是通過拋出異常。
* 並且默認並沒有去處理或者捕獲這些異常。在SpringMVC下需要配置捕獲相應異常來通知用戶信息
* @return
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
Properties properties=new Properties();
//這裏的 /unauthorized 是頁面,不是訪問的路徑
properties.setProperty("org.apache.shiro.authz.UnauthorizedException","/unauthorized");
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","/unauthorized");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
/**
* 解決spring-boot Whitelabel Error Page
* 全局異常頁面配置
* @return
*/
@Component
public class ErrorConfig implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
registry.addErrorPages(error401Page, error404Page, error500Page);
}
}
/**
* Shiro提供了記住我(RememberMe)的功能,比如訪問一些網站時,關閉了瀏覽器下次再打開時還是能記住你是誰,下次訪問時無需再登錄即可訪問,基本流程如下:
* (1)、首先在登錄頁面選中RememberMe然後登錄成功;如果是瀏覽器登錄,一般會把RememberMe的Cookie寫到客戶端並保存下來;
* (2)、關閉瀏覽器再重新打開;會發現瀏覽器還是記住你的;
* (3)、訪問一般的網頁服務器端還是知道你是誰,且能正常訪問
* cookie對象;會話Cookie模板 ,默認爲: JSESSIONID 問題: 與SERVLET容器名衝突,重新定義爲sid或rememberMe,自定義
* @return
*/
@Bean
public SimpleCookie rememberMeCookie(){
//這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//setcookie的httponly屬性如果設爲true的話,會增加對xss防護的安全係數。它有以下特點:
//setcookie()的第七個參數
//設爲true後,只能通過http訪問,javascript無法訪問
//防止xss讀取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
/**
* cookie管理對象;記住我功能,rememberMe管理器
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密鑰 建議每個項目都不一樣 默認AES算法 密鑰長度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
/**
* FormAuthenticationFilter 過濾器 過濾記住我
* @return
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter(){
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//對應前端的checkbox的name = rememberMe
formAuthenticationFilter.setRememberMeParam("rememberMe");
return formAuthenticationFilter;
}
/**
* shiro緩存管理器;
* 需要添加到securityManager中
* @return
*/
/* @Bean
public EhCacheManager ehCacheManager(){
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
return cacheManager;
}*/
/**
* 讓某個實例的某個方法的返回值注入爲Bean的實例
* Spring靜態注入
* @return
*/
/* @Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
// 這一行編譯不通過 博客中securityManager()方法沒有參數
factoryBean.setArguments(new Object[]{ securityManager()});
return factoryBean;
}*/
//===========================shiro配置會話管理配置
/**
* 配置session監聽
* @return
*/
/* @Bean("sessionListener")
public ShiroSessionListener sessionListener(){
ShiroSessionListener sessionListener = new ShiroSessionListener();
return sessionListener;
}*/
/**
* 配置會話ID生成器
* @return
*/
/* @Bean
public SessionIdGenerator sessionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}*/
/**
* SessionDAO的作用是爲Session提供CRUD並進行持久化的一個shiro組件
* MemorySessionDAO 直接在內存中進行會話維護
* EnterpriseCacheSessionDAO 提供了緩存功能的會話維護,默認情況下使用MapCache實現,內部使用ConcurrentHashMap保存緩存的會話。
* @return
*/
/* @Bean
public SessionDAO sessionDAO() {
EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
//使用ehCacheManager
enterpriseCacheSessionDAO.setCacheManager(ehCacheManager());
//設置session緩存的名字 默認爲 shiro-activeSessionCache
enterpriseCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
//sessionId生成器
enterpriseCacheSessionDAO.setSessionIdGenerator(sessionIdGenerator());
return enterpriseCacheSessionDAO;
}*/
/**
* 配置保存sessionId的cookie
* 注意:這裏的cookie 不是上面的記住我 cookie 記住我需要一個cookie session管理 也需要自己的cookie
* @return
*/
/* @Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
//這個參數是cookie的名稱
SimpleCookie simpleCookie = new SimpleCookie("sid");
//setcookie的httponly屬性如果設爲true的話,會增加對xss防護的安全係數。它有以下特點:
//setcookie()的第七個參數
//設爲true後,只能通過http訪問,javascript無法訪問
//防止xss讀取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//maxAge=-1表示瀏覽器關閉時失效此Cookie
simpleCookie.setMaxAge(-1);
return simpleCookie;
}*/
/**
* 配置會話管理器,設定會話超時及保存
* @return
*/
/* @Bean("sessionManager")
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//配置監聽
listeners.add(sessionListener());
sessionManager.setSessionListeners(listeners);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setSessionDAO(sessionDAO());
sessionManager.setCacheManager(ehCacheManager());
//如果用戶如果不點註銷,直接關閉瀏覽器,不能夠進行session的清空處理,所以爲了防止這樣的問題,還需要增加有一個會話的驗證調度
//全局會話超時時間(單位毫秒),默認30分鐘 暫時設置爲10秒鐘 用來測試
sessionManager.setGlobalSessionTimeout(10000);
//sessionManager.setGlobalSessionTimeout(1800000);
//是否開啓刪除無效的session對象 默認爲true
sessionManager.setDeleteInvalidSessions(true);
//是否開啓定時調度器進行檢測過期session 默認爲true
sessionManager.setSessionValidationSchedulerEnabled(true);
//設置session失效的掃描時間, 清理用戶直接關閉瀏覽器造成的孤立會話 默認爲 1個小時
//設置該屬性 就不需要設置 ExecutorServiceSessionValidationScheduler 底層也是默認自動調用ExecutorServiceSessionValidationScheduler
//暫時設置爲 5秒 用來測試
sessionManager.setSessionValidationInterval(5000);
// sessionManager.setSessionValidationInterval(3600000);
//取消url 後面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}*/
}
3:下一下我們自定義Ream
package com.guoheng.safe.config;
import com.guoheng.safe.sys.mapper.user.*;
import com.guoheng.safe.sys.model.*;
import org.apache.shiro.authc.*;
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.context.annotation.Lazy;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
/**
* @author: Fbl
* @date: 2019/6/14
* @description: 在Shiro中,最終是通過Realm來獲取應用程序中的用戶、角色及權限信息的
* 在Realm中會直接從我們的數據源中獲取Shiro需要的驗證信息。可以說,Realm是專用於安全框架的DAO.
*/
public class ShiroRealm extends AuthorizingRealm {
@Lazy
@Autowired
UserMapper userMapper;
@Lazy
@Autowired
RoleMapper roleMapper;
@Lazy
@Autowired
UserRoleMapper userRoleMapper;
@Lazy
@Autowired
PermissionsMapper permissionsMapper;
@Lazy
@Autowired
RolePermissionsMapper rolePermissionsMapper;
/**
* 驗證用戶身份
*
* @param authenticationToken 用戶信息
* @return AuthenticationInfo
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取用戶名密碼 第一種方式
//String username = (String) authenticationToken.getPrincipal();
//String password = new String((char[]) authenticationToken.getCredentials());
//獲取用戶名 密碼 第二種方式
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String userAccount = usernamePasswordToken.getUsername();
String pwd = new String(usernamePasswordToken.getPassword());
User user = userMapper.login(userAccount);
if (null == user){
throw new UnknownAccountException("用戶名不存在!");
}
if (!user.getPassword().equals(pwd)){
throw new IncorrectCredentialsException("用戶名或密碼錯誤!");
}
//可以在這裏直接對用戶名校驗,或者調用 CredentialsMatcher 校驗
//調用 CredentialsMatcher 校驗 還需要創建一個類 繼承CredentialsMatcher 如果在上面校驗了,這個就不需要了
//配置自定義權限登錄器 參考博客:
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
return info;
}
/**
* 授權用戶權限
* 授權的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>標籤的時候調用的
* 它會去檢測shiro框架中的權限(這裏的permissions)是否包含有該標籤的name值,如果有,裏面的內容顯示
* 如果沒有,裏面的內容不予顯示(這就完成了對於權限的認證.)
* <p>
* shiro的權限授權是通過繼承AuthorizingRealm抽象類,重載doGetAuthorizationInfo();
* 當訪問到頁面的時候,鏈接配置了相應的權限或者shiro標籤纔會執行此方法否則不會執行
* 所以如果只是簡單的身份認證沒有權限的控制的話,那麼這個方法可以不進行實現,直接返回null即可。
* <p>
* 在這個方法中主要是使用類:SimpleAuthorizationInfo 進行角色的添加和權限的添加。
* authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
* <p>
* 當然也可以添加set集合:roles是從數據庫查詢的當前用戶的角色,stringPermissions是從數據庫查詢的當前用戶對應的權限
* authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
* <p>
* 就是說如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[權限添加]");
* 就說明訪問/add這個鏈接必須要有“權限添加”這個權限纔可以訪問
* <p>
* 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002],perms[權限添加]");
* 就說明訪問/add這個鏈接必須要有 "權限添加" 這個權限和具有 "100002" 這個角色纔可以訪問
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取登錄用戶名
User user = (User) principalCollection.getPrimaryPrincipal();
// User user = userMapper.login(userAccount);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 查詢用戶角色
Example userRoleExample = new Example(UserRole.class);
userRoleExample.createCriteria().andEqualTo("userId",user.getId());
List<UserRole> userRoles = userRoleMapper.selectByExample(userRoleExample);
for (UserRole userRole : userRoles) {
Integer roleId = userRole.getRoleId();
// 查詢角色
Role role = roleMapper.selectByPrimaryKey(roleId);
// 添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
// 查詢角色權限
Example rolePermissionsExample = new Example(RolePermissions.class);
rolePermissionsExample.createCriteria().andEqualTo("roleId",roleId);
List<RolePermissions> rolePermissions = rolePermissionsMapper.selectByExample(rolePermissionsExample);
for (RolePermissions rolePermission : rolePermissions) {
Integer permissionsId = rolePermission.getPermissionsId();
Permissions permissions = permissionsMapper.selectByPrimaryKey(permissionsId);
// 添加權限
simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsNo());
}
}
return simpleAuthorizationInfo;
}
/**
* 重寫方法,清除當前用戶的的 授權緩存
*
* @param principals
*/
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
/**
* 重寫方法,清除當前用戶的 認證緩存
*
* @param principals
*/
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
/**
* 自定義方法:清除所有 授權緩存
*/
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
/**
* 自定義方法:清除所有 認證緩存
*/
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
/**
* 自定義方法:清除所有的 認證緩存 和 授權緩存
*/
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
}
4:登錄Controller
package com.guoheng.safe.sys.controller;
import com.guoheng.safe.common.base.Result;
import com.guoheng.safe.common.enums.ErrorCodeEnum;
import com.guoheng.safe.sys.dto.UserLoginDTO;
import com.guoheng.safe.sys.service.UserService;
import io.swagger.annotations.Api;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 類功能描述:LoginController
*
* @author Eternal
* @date 2018/7/22 0:15
*/
@RestController
@Validated
@Api(value = "用戶登錄")
public class LoginController {
@Resource
UserService userService;
@PostMapping(value = "user/login")
public Result login(@RequestBody @Valid UserLoginDTO userLoginDTO) {
try {
Result login = userService.login(userLoginDTO);
return Result.success(login);
} catch (UnknownAccountException e) {
return Result.failure(ErrorCodeEnum.SYS_ERR_USER_NON_EXISTENT);
} catch (IncorrectCredentialsException e) {
return Result.failure(ErrorCodeEnum.SYS_ERR_USER_PASSWORD_ERROR);
} catch (Exception e) {
e.printStackTrace();
return Result.failure(ErrorCodeEnum.SYS_ERR_PROCEDURE);
}
}
}
5:登錄service
package com.guoheng.safe.sys.service.impl;
import cn.hutool.crypto.digest.DigestUtil;
import com.guoheng.safe.common.base.Result;
import com.guoheng.safe.sys.dto.ReturnLoginDTO;
import com.guoheng.safe.sys.dto.UserDTO;
import com.guoheng.safe.sys.dto.UserLoginDTO;
import com.guoheng.safe.sys.mapper.basicinfo.BasicInfoMapper;
import com.guoheng.safe.sys.mapper.user.UserMapper;
import com.guoheng.safe.sys.model.User;
import com.guoheng.safe.sys.model.basicinfo.BasicInfo;
import com.guoheng.safe.sys.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 類功能描述: UserServiceImpl
*
* @author Eternal
* @date 2019-32-15 11:32
*/
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserMapper userMapper;
@Resource
BasicInfoMapper basicInfoMapper;
@Override
public Result login(UserLoginDTO loginDTO) {
// 摘要加密算法md5加密
String password = DigestUtil.md5Hex(loginDTO.getPassword());
// shiro框架驗證登錄
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginDTO.getUserAccount(), password);
Subject subject = SecurityUtils.getSubject();
// shiro登錄操作
subject.login(usernamePasswordToken);
// 獲取用戶信息
User user = (User) subject.getPrincipal();
// 查詢用戶信息
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setAccount(user.getUserAccount());
// 返回廠區信息
Integer basicInfoId = user.getBasicInfoId();
BasicInfo basicInfo = basicInfoMapper.selectById(basicInfoId);
ReturnLoginDTO returnLoginDTO = new ReturnLoginDTO();
returnLoginDTO.setUserDTO(userDTO);
returnLoginDTO.setBasicInfo(basicInfo);
return Result.success(returnLoginDTO);
}
}
其中我在我項目的接口上添加了權限註解:只有root角色,擁有select權限的人,纔可以訪問此接口
@RequiresRoles({"root"})
@RequiresPermissions({"select"})
@PostMapping(value = "map/hazard")
public Result getHazards(@RequestBody @Valid GetHazardsDTO getHazardsDTO){
return hazardMapService.getHazards(getHazardsDTO);
}
我們看一下效果,先使用root用戶登錄:
登錄成功,訪問需要權限的接口:
然後我們是用user用戶登錄:
登錄成功,訪問需要權限的接口:
這裏的:沒有通過權限驗證!是我用異常攔截器配置的,沒有配置的shiro會拋出異常,捕捉一下並返回相應信息就可以了
異常攔截類:
package com.guoheng.safe.common.exception;
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;
/**
* 類功能描述: TODO
*
* @author fbl
* @date 2019-11-26 11:20
*/
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
log.error("沒有通過權限驗證!", e);
return "沒有通過權限驗證!";
}
}