javascript
Springboot系列之Shiro、JWT、Redis 进行认证鉴权
Springboot系列之Shiro、JWT、Redis 進行認證鑒權
Shiro架構
Apache Shiro是一個輕量級的安全框架
Shiro可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE環境,也可以用在JavaEE環境。 Shiro可以幫助我們完成:認證、授權、加密、會話管理、與Web集成、緩存等。其基本功能點如下圖所示:
- Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;
- Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;
- Session Manager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的;
- Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
- Web Support:Web支持,可以非常容易的集成到Web環境;
- Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;
- Concurrency:shiro支持多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;
- Testing:提供測試支持;
- Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
- Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了。
Shiro不會去維護用戶、維護權限;這些需要我們自己去設計/提供;然后通過相應的接口注入給Shiro即可。
接下來我們分別從外部和內部來看看Shiro的架構,對于一個好的框架,從外部來看應該具有非常簡單易于使用的API, 且API契約明確;從內部來看的話,其應該有一個可擴展的架構,即非常容易插入用戶自定義實現,因為任何框架都不能滿足所有需求。
可以看到:應用代碼直接交互的對象是Subject,也就是說Shiro的對外API核心就是Subject。
- Subject:主體,代表了當前“用戶”,這個用戶不一定是一個具體的人,與當前應用交互的任何東西都是Subject,如網絡爬蟲,機器人等;即一個抽象概念;所有Subject都綁定到SecurityManager,與Subject的所有交互都會委托給SecurityManager;可以把Subject認為是一個門面;SecurityManager才是實際的執行者;
- SecurityManager:安全管理器;即所有與安全有關的操作都會與SecurityManager交互;且它管理著所有Subject;可以看出它是Shiro的核心,它負責與后邊介紹的其他組件進行交互,如果學習過SpringMVC,你可以把它看成DispatcherServlet前端控制器;
- Realm:域,Shiro從從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應的角色/權限進行驗證用戶是否能進行操作;可以把Realm看成DataSource,即安全數據源。
也就是說對于我們而言,最簡單的一個Shiro應用:
從以上也可以看出,Shiro不提供維護用戶/權限,而是通過Realm讓開發人員自己注入。
接下來我們來從Shiro內部來看下Shiro的架構,如下圖所示:
- Subject:主體,可以看到主體可以是任何可以與應用交互的“用戶”;
- SecurityManager:相當于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心臟;所有具體的交互都通過SecurityManager進行控制;它管理著所有Subject、且負責進行認證和授權、及會話、緩存的管理。
- Authenticator:認證器,負責主體認證的,這是一個擴展點,如果用戶覺得Shiro默認的不好,可以自定義實現;其需要認證策略(Authentication Strategy),即什么情況下算用戶認證通過了;
- Authorizer:授權器,或者訪問控制器,用來決定主體是否有權限進行相應的操作;即控制著用戶能訪問應用中的哪些功能;
- Realm:可以有1個或多個Realm,可以認為是安全實體數據源,即用于獲取安全實體的;可以是JDBC實現,也可以是LDAP實現,或者內存實現等等;由用戶提供;注意:Shiro不知道你的用戶/權限存儲在哪及以何種格式存儲;所以我們一般在應用中都需要實現自己的Realm;
- SessionManager:如果寫過Servlet就應該知道Session的概念,Session呢需要有人去管理它的生命周期,這個組件就是SessionManager;而Shiro并不僅僅可以用在Web環境,也可以用在如普通的JavaSE環境、EJB等環境;所有呢,Shiro就抽象了一個自己的Session來管理主體與應用之間交互的數據;這樣的話,比如我們在Web環境用,剛開始是一臺Web服務器;接著又上了臺EJB服務器;這時想把兩臺服務器的會話數據放到一個地方,這個時候就可以實現自己的分布式會話(如把數據放到Memcached服務器);
- SessionDAO:DAO大家都用過,數據訪問對象,用于會話的CRUD,比如我們想把Session保存到數據庫,那么可以實現自己的SessionDAO,通過如JDBC寫到數據庫;比如想把Session放到Memcached中,可以實現自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache進行緩存,以提高性能;
- CacheManager:緩存控制器,來管理如用戶、角色、權限等的緩存的;因為這些數據基本上很少去改變,放到緩存中后可以提高訪問的性能
- Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于如密碼加密/解密的。
下面就開始代碼實現Springboot Shiro JWT Redis認證鑒權(核心代碼如下)
?
package com.kongliand.shiro.config;import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.*;import javax.annotation.Resource; import java.lang.reflect.Method;import java.time.Duration; import java.util.Arrays;import static java.util.Collections.singletonMap;/*** redis核心配置類*/ @Configuration @EnableCaching // 開啟緩存支持 public class RedisConfig extends CachingConfigurerSupport {@Resourceprivate LettuceConnectionFactory lettuceConnectionFactory;/*** 自定義策略生成的key* 自定義的緩存key的生成策略 若想使用這個key* 只需要講注解上keyGenerator的值設置為keyGenerator即可</br>*/@Override@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getDeclaringClass().getName());Arrays.stream(params).map(Object::toString).forEach(sb::append);return sb.toString();}};}/*** RedisTemplate配置*/@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {// 設置序列化Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);om.enableDefaultTyping(DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置redisTemplateRedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);RedisSerializer<?> stringSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(stringSerializer);// key序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化redisTemplate.afterPropertiesSet();return redisTemplate;}/*** 緩存配置管理器*/@Beanpublic CacheManager cacheManager(LettuceConnectionFactory factory) {// 配置序列化RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));// 以鎖寫入的方式創建RedisCacheWriter對象//RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);// 創建默認緩存配置對象/* 默認配置,設置緩存有效期 1小時*///RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));/* 配置test的超時時間為120s*/RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration).withInitialCacheConfigurations(singletonMap("test", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(120)).disableCachingNullValues())).transactionAware().build();return cacheManager;}} package com.kongliand.shiro.config;import com.kongliand.shiro.filter.JwtFilter; import com.kongliand.shiro.shiro.ShiroRealm; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; import org.apache.shiro.mgt.DefaultSubjectDAO; 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.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn;import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map;/*** @desc: shiro 配置類* @author kevin*/@Configuration public class ShiroConfig {/*** Filter Chain定義說明* <p>* 1、一個URL可以配置多個Filter,使用逗號分隔* 2、當設置多個過濾器時,全部驗證通過,才視為通過* 3、部分過濾器可指定參數,如perms,roles*/@Bean("shiroFilter")public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);// 攔截器Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();// 配置不會被攔截的鏈接 順序判斷filterChainDefinitionMap.put("/sys/login", "anon"); //登錄接口排除filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除filterChainDefinitionMap.put("/", "anon");filterChainDefinitionMap.put("/**/*.js", "anon");filterChainDefinitionMap.put("/**/*.css", "anon");filterChainDefinitionMap.put("/**/*.html", "anon");filterChainDefinitionMap.put("/**/*.jpg", "anon");filterChainDefinitionMap.put("/**/*.png", "anon");filterChainDefinitionMap.put("/**/*.ico", "anon");filterChainDefinitionMap.put("/druid/**", "anon");filterChainDefinitionMap.put("/user/test", "anon"); //測試// 添加自己的過濾器并且取名為jwtMap<String, Filter> filterMap = new HashMap<String, Filter>(1);filterMap.put("jwt", new JwtFilter());shiroFilterFactoryBean.setFilters(filterMap);// <!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊filterChainDefinitionMap.put("/**", "jwt");// 未授權界面返回JSONshiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403");shiroFilterFactoryBean.setLoginUrl("/sys/common/403");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Bean("securityManager")public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);/** 關閉shiro自帶的session,詳情見文檔* http://shiro.apache.org/session-management.html#SessionManagement-* StatelessApplications%28Sessionless%29*/DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);return securityManager;}/*** 下面的代碼是添加注解支持** @return*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}3.鑒權登錄攔截器
package com.kongliand.shiro.filter;import com.kongliand.shiro.constant.CommonConstant; import com.kongliand.shiro.entity.JwtToken; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.RequestMethod;import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;/*** 鑒權登錄攔截器**/ @Slf4j public class JwtFilter extends BasicHttpAuthenticationFilter {/*** 執行登錄認證** @param request* @param response* @param mappedValue* @return*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {try {executeLogin(request, response);return true;} catch (Exception e) {throw new AuthenticationException(e.getMessage());}}/****/@Overrideprotected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;String token = httpServletRequest.getHeader(CommonConstant.ACCESS_TOKEN);JwtToken jwtToken = new JwtToken(token);// 提交給realm進行登入,如果錯誤他會拋出異常并被捕獲getSubject(request, response).login(jwtToken);// 如果沒有拋出異常則代表登入成功,返回truereturn true;}/*** 對跨域提供支持*/@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;HttpServletResponse httpServletResponse = (HttpServletResponse) response;httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));// 跨域時會首先發送一個option請求,這里我們給option請求直接返回正常狀態if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpServletResponse.setStatus(HttpStatus.OK.value());return false;}return super.preHandle(request, response);} }4.用戶登錄鑒權和獲取用戶授權
package com.kongliand.shiro.shiro;import com.kongliand.shiro.constant.CommonConstant; import com.kongliand.shiro.entity.JwtToken; import com.kongliand.shiro.entity.SysUser; import com.kongliand.shiro.service.ISysUserService; import com.kongliand.shiro.util.CommonUtils; import com.kongliand.shiro.util.JwtUtil; import com.kongliand.shiro.util.RedisUtil; import com.kongliand.shiro.util.SpringContextUtils; import lombok.extern.slf4j.Slf4j; 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.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component;import java.util.Set;/*** 用戶登錄鑒權和獲取用戶授權*/ @Component @Slf4j public class ShiroRealm extends AuthorizingRealm {@Autowired@Lazyprivate ISysUserService sysUserService;@Autowired@Lazyprivate RedisUtil redisUtil;/*** 必須重寫此方法,不然Shiro會報錯*/@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof JwtToken;}/*** 功能: 獲取用戶權限信息,包括角色以及權限。只有當觸發檢測用戶權限時才會調用此方法,例如checkRole,checkPermission** @param principals token* @return AuthorizationInfo 權限信息*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {log.info("————權限認證 [ roles、permissions]————");SysUser sysUser = null;String username = null;if (principals != null) {sysUser = (SysUser) principals.getPrimaryPrincipal();username = sysUser.getUserName();}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 設置用戶擁有的角色集合,比如“admin,test”Set<String> roleSet = sysUserService.getUserRolesSet(username);info.setRoles(roleSet);// 設置用戶擁有的權限集合,比如“sys:role:add,sys:user:add”Set<String> permissionSet = sysUserService.getUserPermissionsSet(username);info.addStringPermissions(permissionSet);return info;}/*** 功能: 用來進行身份認證,也就是說驗證用戶輸入的賬號和密碼是否正確,獲取身份驗證信息,錯誤拋出異常** @param auth 用戶身份信息 token* @return 返回封裝了用戶信息的 AuthenticationInfo 實例*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {String token = (String) auth.getCredentials();if (token == null) {log.info("————————身份認證失敗——————————IP地址: " + CommonUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));throw new AuthenticationException("token為空!");}// 校驗token有效性SysUser loginUser = this.checkUserTokenIsEffect(token);return new SimpleAuthenticationInfo(loginUser, token, getName());}/*** 校驗token的有效性** @param token*/public SysUser checkUserTokenIsEffect(String token) throws AuthenticationException {// 解密獲得username,用于和數據庫進行對比String username = JwtUtil.getUsername(token);if (username == null) {throw new AuthenticationException("token非法無效!");}// 查詢用戶信息SysUser loginUser = new SysUser();SysUser sysUser = sysUserService.getUserByName(username);if (sysUser == null) {throw new AuthenticationException("用戶不存在!");}// 校驗token是否超時失效 & 或者賬號密碼是否錯誤if (!jwtTokenRefresh(token, username, sysUser.getPassWord())) {throw new AuthenticationException("Token失效請重新登錄!");}// 判斷用戶狀態if (!"0".equals(sysUser.getDelFlag())) {throw new AuthenticationException("賬號已被刪除,請聯系管理員!");}BeanUtils.copyProperties(sysUser, loginUser);return loginUser;}/*** JWTToken刷新生命周期 (解決用戶一直在線操作,提供Token失效問題)* 1、登錄成功后將用戶的JWT生成的Token作為k、v存儲到cache緩存里面(這時候k、v值一樣)* 2、當該用戶再次請求時,通過JWTFilter層層校驗之后會進入到doGetAuthenticationInfo進行身份驗證* 3、當該用戶這次請求JWTToken值還在生命周期內,則會通過重新PUT的方式k、v都為Token值,緩存中的token值生命周期時間重新計算(這時候k、v值一樣)* 4、當該用戶這次請求jwt生成的token值已經超時,但該token對應cache中的k還是存在,則表示該用戶一直在操作只是JWT的token失效了,程序會給token對應的k映射的v值重新生成JWTToken并覆蓋v值,該緩存生命周期重新計算* 5、當該用戶這次請求jwt在生成的token值已經超時,并在cache中不存在對應的k,則表示該用戶賬戶空閑超時,返回用戶信息已失效,請重新登錄。* 6、每次當返回為true情況下,都會給Response的Header中設置Authorization,該Authorization映射的v為cache對應的v值。* 7、注:當前端接收到Response的Header中的Authorization值會存儲起來,作為以后請求token使用** @param userName* @param passWord* @return*/public boolean jwtTokenRefresh(String token, String userName, String passWord) {String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));if (CommonUtils.isNotEmpty(cacheToken)) {// 校驗token有效性if (!JwtUtil.verify(cacheToken, userName, passWord)) {String newAuthorization = JwtUtil.sign(userName, passWord);redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);// 設置超時時間redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);} else {redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);// 設置超時時間redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);}return true;}return false;}}[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Wdbq8hpS-1639299147506)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
5.application.yml配置信息
server:port: 8088spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://127.0.0.1:3306/jwt?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTCusername: rootpassword: admin123initial-size: 10max-active: 100min-idle: 10max-wait: 60000pool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000#validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsestat-view-servlet:enabled: trueurl-pattern: /druid/*login-username: adminlogin-password: adminfilter:stat:log-slow-sql: trueslow-sql-millis: 1000merge-sql: falsewall:config:multi-statement-allow: true#redis配置redis:database: 0host: 127.0.0.1lettuce:pool:max-active: 8 #最大連接數據庫連接數,設 0 為沒有限制max-idle: 8 #最大等待連接中的數量,設 0 為沒有限制max-wait: -1ms #最大建立連接等待時間。如果超過此時間將接到異常。設為-1表示無限制。min-idle: 0 #最小等待連接中的數量,設 0 為沒有限制shutdown-timeout: 100mspassword: ''port: 6379#mybatis plus設置 mybatis-plus:type-aliases-package: com.kongliand.shiro.entitymapper-locations: classpath:mapper/*.xmlglobal-config:banner: falsedb-config:#主鍵類型id-type: auto# 默認數據庫表下劃線命名table-underline: trueconfiguration:map-underscore-to-camel-case: true# 這個配置會將執行的sql打印出來,在開發或測試的時候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#日志配置 logging:level:com.kongliand.shiro.mapper: debug最后開始驗證一下
1.獲取token
2.根據token請求接口
總結
以上是生活随笔為你收集整理的Springboot系列之Shiro、JWT、Redis 进行认证鉴权的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3精要(62)-编译与解释实
- 下一篇: npm的镜像替换淘宝