日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)...

發(fā)布時(shí)間:2025/3/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文主要考慮單點(diǎn)登錄場(chǎng)景,登錄由其他系統(tǒng)負(fù)責(zé),業(yè)務(wù)子系統(tǒng)只使用shiro進(jìn)行菜單和功能權(quán)限校驗(yàn),登錄信息通過token從redis取得,這樣登錄驗(yàn)證和授權(quán)就相互解耦了。

用戶、角色、權(quán)限進(jìn)行集中式管理。網(wǎng)上不少這樣的提問,但是沒有解決方案、抑或只是說明如何做,并沒有完整的現(xiàn)成解決方法。

Apache Shiro 是Java 的一個(gè)安全框架,和Spring Security并駕齊驅(qū),能夠很好的和freemarker、thymeleaf無縫集成,同時(shí)能夠無縫的應(yīng)用于restful方法,這一點(diǎn)很重要,能夠很方便的進(jìn)行維護(hù)。

Shiro的架構(gòu)

?

其中認(rèn)證和授權(quán)都是必須的,而不是可選的,這一點(diǎn)很多文檔并沒有很明確的說明,準(zhǔn)確的說不管是否用三方登錄驗(yàn)證,使用shiro的話,shiro的整個(gè)骨架都得過一遍,一開始我也是認(rèn)為認(rèn)證是可以跳過的,為此浪費(fèi)了幾個(gè)小時(shí)。

會(huì)話和緩存可以使用redis替換默認(rèn)的實(shí)現(xiàn)。

掌握shiro必須理解下列關(guān)鍵概念,這一點(diǎn)可能是一開始不理解shiro機(jī)制的時(shí)候覺得難以找到套路的原因。

?

  • Subject:主體,代表了當(dāng)前“用戶”,這個(gè)用戶不一定是一個(gè)具體的人,與當(dāng)前應(yīng)用交互的任何東西都是Subject,如網(wǎng)絡(luò)爬蟲,機(jī)器人等;即一個(gè)抽象概念;所有Subject 都綁定到SecurityManager,與Subject的所有交互都會(huì)委托給SecurityManager;可以把Subject認(rèn)為是一個(gè)門面;SecurityManager才是實(shí)際的執(zhí)行者;
  • SecurityManager:安全管理器;即所有與安全有關(guān)的操作都會(huì)與SecurityManager 交互;且它管理著所有Subject;可以看出它是Shiro 的核心,它負(fù)責(zé)與后邊介紹的其他組件進(jìn)行交互,如果學(xué)習(xí)過SpringMVC,你可以把它看成DispatcherServlet前端控制器;
  • Realm:域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶、角色、權(quán)限),就是說SecurityManager要驗(yàn)證用戶身份,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作;可以把Realm看成DataSource,即安全數(shù)據(jù)源。

subject其實(shí)是mvc負(fù)責(zé)生成的,例如spring mvc,以認(rèn)證為例:

其中.login是在spring mvc域,Subject在org.apache.shiro.subject.Subject.Builder.buildSubject()生成。

?

對(duì)于我們而言,最簡(jiǎn)單的一個(gè)Shiro 應(yīng)用:
1、應(yīng)用代碼通過Subject來進(jìn)行認(rèn)證和授權(quán),而Subject又委托給SecurityManager;
2、我們需要給Shiro 的SecurityManager 注入Realm,從而讓SecurityManager 能得到合法的用戶及其權(quán)限進(jìn)行判斷。

現(xiàn)在開始講解完整的實(shí)現(xiàn)。

?maven依賴

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-aspectj</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version></dependency><dependency><groupId>net.mingsoft</groupId><artifactId>shiro-freemarker-tags</artifactId><version>1.0.0</version></dependency>

?

spring配置文件,application-mvc.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:util="http://www.springframework.org/schema/util"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"xmlns:sharding="http://shardingjdbc.io/schema/shardingjdbc/sharding"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.3.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.3.xsdhttp://shardingjdbc.io/schema/shardingjdbc/shardinghttp://shardingjdbc.io/schema/shardingjdbc/sharding/sharding.xsd"default-lazy-init="true"><context:property-placeholder location="classpath*:jrescloud.properties" ignore-unresolvable="true" order="1"/><aop:aspectj-autoproxy proxy-target-class="true" /><!-- 安全集成 --><bean id="$user" class="com.hundsun.ta.web.security.client.filter.UserFilter" /><bean id="$authc" class="com.hundsun.ta.web.security.client.filter.FormAuthenticationFilter" /><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager" /><property name="loginUrl" value="/console.html" /><property name="filterChainDefinitions"><value>/ = anon/login.html = $authc/static/** = anon/** = $user</value></property></bean><!-- Enable Shiro Annotations for Spring-configured beans. Only run after --><!-- the lifecycleBeanProcessor has run: --><bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"><property name="usePrefix" value="true" /></bean><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/></bean><!-- Security Manager --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="authenticator" ref="authenticator" /><property name="realm" ref="defautlRealm" /></bean><bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"><property name="authenticationListeners"><list><bean class="com.hundsun.ta.web.security.client.listener.DefaultAuthenticationListener" /></list></property></bean><bean id="defautlRealm" class="com.hundsun.ta.web.security.client.realm.DefaultAuthorizingRealm"><property name="cacheManager" ref="cacheManager"/><property name="credentialsMatcher" ref="passwordMatcher" /></bean><bean id="passwordMatcher" class="org.apache.shiro.authc.credential.PasswordMatcher"><property name="passwordService" ref="passwordService"/></bean><bean id="passwordService" class="org.apache.shiro.authc.credential.DefaultPasswordService"><property name="hashService"><bean class="org.apache.shiro.crypto.hash.DefaultHashService"><property name="hashAlgorithmName" value="MD5"/></bean></property></bean><bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" /><bean id="simpleCredentialsMatcher" class="org.apache.shiro.authc.credential.SimpleCredentialsMatcher"/><bean id="allowAllCredentialsMatcher" class="org.apache.shiro.authc.credential.AllowAllCredentialsMatcher"/> </beans>

spring bean配置

@Configuration @ImportResource(locations = { "classpath*:application-mvc.xml" }) public class BaseWebAppConfig {@Beanpublic FilterRegistrationBean filterRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new DelegatingFilterProxy("shiroFilter"));registration.addUrlPatterns("/*");registration.addInitParameter("targetFilterLifecycle", "true");registration.addInitParameter("staticSecurityManagerEnabled", "true");registration.setName("shiroFilter");registration.setEnabled(true);return registration;}
@Beanpublic FreeMarkerConfigurer freemarkerConfig() throws IOException, TemplateException {FreeMarkerConfigExtend configurer = new FreeMarkerConfigExtend();configurer.setTemplateLoaderPath("classpath:/templates");configurer.setDefaultEncoding("UTF-8");Map<String, Object> freemarkerVariables = new HashMap<>();freemarkerVariables.put("appServiceUrl", env.getProperty(BaseConfig.TaBaseConfigConst.APP_SERVICE_URL));configurer.setFreemarkerVariables(freemarkerVariables);return configurer;}@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)public FreeMarkerViewResolver getFreemarkViewResolver() {FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();freeMarkerViewResolver.setCache(false);freeMarkerViewResolver.setSuffix(".html");freeMarkerViewResolver.setContentType("text/html; charset=UTF-8");freeMarkerViewResolver.setAllowRequestOverride(false);freeMarkerViewResolver.setViewClass(FreeMarkerView.class);freeMarkerViewResolver.setExposeSpringMacroHelpers(false);freeMarkerViewResolver.setExposeRequestAttributes(false);freeMarkerViewResolver.setExposeSessionAttributes(false);return freeMarkerViewResolver;} }

java bean

import java.io.IOException;import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;import com.jagregory.shiro.freemarker.ShiroTags;import freemarker.template.TemplateException;public class FreeMarkerConfigExtend extends FreeMarkerConfigurer {@Overridepublic void afterPropertiesSet() throws IOException, TemplateException {super.afterPropertiesSet();this.getConfiguration().setSharedVariable("shiro", new ShiroTags());} }

登錄判斷攔截器

@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String path = request.getContextPath().length() > 1 ? request.getRequestURI().replace(request.getContextPath(), "") : request.getRequestURI();// 登錄認(rèn)證,模擬方便這里寫死訪問頁面和用戶名/密碼if (path.equals("/console.html")) {SecurityUtils.setSecurityManager(SpringContextHolder.getBean(DefaultWebSecurityManager.class));//得到Subject及創(chuàng)建用戶名/密碼身份驗(yàn)證Token(即用戶身份/憑證) Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("system","1"); //登錄密碼 subject.login(token);}}

filter實(shí)現(xiàn)

import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject;/*** shiro單點(diǎn)登錄認(rèn)證 * <p>Title: FormAuthenticationFilter</p> * <p>Description: </p> * @author zjhua * @date 2019年1月28日*/ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return true;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {return true;}/*** 登錄成功后,將原來的session注銷,新增新的session* @param token* @param subject* @param request* @param response* @return* @throws Exception*/protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception{return true;} } import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;/*** 用戶登錄校驗(yàn)過濾器*/ public class UserFilter extends org.apache.shiro.web.filter.authc.UserFilter {@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return true;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {return true;} } import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationListener; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory;/*** 默認(rèn)認(rèn)證實(shí)現(xiàn) * <p>Title: DefaultAuthenticationListener</p> * <p>Description: </p> * @author zjhua* @date 2019年1月28日*/ public class DefaultAuthenticationListener implements AuthenticationListener {private static final Logger logger = LoggerFactory.getLogger(DefaultAuthenticationListener.class);@Overridepublic void onSuccess(AuthenticationToken token, AuthenticationInfo info) {// NOP }@Overridepublic void onFailure(AuthenticationToken token, AuthenticationException ae) {// NOP }@Overridepublic void onLogout(PrincipalCollection principals) {// NOP }} import java.util.Arrays; import java.util.Collections; import java.util.List;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.crypto.hash.DefaultHashService; import org.apache.shiro.crypto.hash.Hash; import org.apache.shiro.crypto.hash.HashRequest; import org.apache.shiro.crypto.hash.HashService; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Base64Utils; import org.springframework.util.CollectionUtils;import com.hundsun.ta.utils.JsonUtils; import com.hundsun.ta.utils.RedisUtil;/*** 默認(rèn)授權(quán)實(shí)現(xiàn)*/ public class DefaultAuthorizingRealm extends AuthorizingRealm {private static final String REALM_NAME = "default";@Autowiredprivate RedisUtil redisUtil;@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {char[] pwd = new char[] {'x'};// HashService hashService = new DefaultHashService();
// 模擬方便,這里寫死用戶名/密碼return new SimpleAuthenticationInfo("system", new Md5Hash("1"), REALM_NAME);}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 模擬方便,這里寫死用戶名List
<String> authList = JsonUtils.json2ListAppointed(redisUtil.get("sid:", "1").toString(), String.class);authorizationInfo.addStringPermissions(authList);return authorizationInfo;}@Overrideprotected Object getAuthorizationCacheKey(PrincipalCollection principals) {//// Project project = getProjectFromWebSubject();// if(project == null) {return super.getAuthorizationCacheKey(principals);// }// return principals.getPrimaryPrincipal().toString() + "#" + project.getId(); } }

上述就是完整的代碼了,這樣shiro就相當(dāng)于實(shí)現(xiàn)了只有權(quán)限、沒有認(rèn)證過程,因?yàn)槲覀兛梢曰趖oken得到認(rèn)證信息直接完成。

示例:

@RequiresPermissions("order:view")@RequestMapping("/demo/vue-page")public String vuePage(Model m) {
return "/demo/vue-page" }
<@shiro.hasPermission name="order:add"><el-button size="small" @click="showAddDialog">新增(彈框模式)</el-button></@shiro.hasPermission>

?上述配置完成之后,整個(gè)就打通了,但是還存在一個(gè)問題,就是在進(jìn)行權(quán)限校驗(yàn)的時(shí)候,shiro是把權(quán)限保存在org.apache.shiro.cache.MemoryConstrainedCacheManager中,它是JVM本地緩存,這會(huì)導(dǎo)致基礎(chǔ)系統(tǒng)修改之后,權(quán)限無法生效,因?yàn)閟hiro的默認(rèn)機(jī)制是退出然后重新登錄才會(huì)去取。對(duì)此有兩種解決方法:一種是自己實(shí)現(xiàn)緩存(本來想集成shiro-redis,發(fā)現(xiàn)還是自己控制最合適),另外一種是禁用緩存。此處先說明第二種。將defautlRealm改為如下即可:

<bean id="defautlRealm" class="com.hundsun.ta.web.security.client.realm.DefaultAuthorizingRealm"><!-- <property name="cacheManager" ref="cacheManager"/> --><property name="authorizationCachingEnabled" value="false"></property><property name="credentialsMatcher" ref="passwordMatcher" /></bean>

shiro是在org.apache.shiro.realm.AuthorizingRealm.getAuthorizationInfo(PrincipalCollection)判斷緩存的:

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {if (principals == null) {return null;}AuthorizationInfo info = null;if (log.isTraceEnabled()) {log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");}Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();if (cache != null) {if (log.isTraceEnabled()) {log.trace("Attempting to retrieve the AuthorizationInfo from cache.");}
// 這里就會(huì)調(diào)用子類的實(shí)現(xiàn),也就是我們的com.XXX.XXX.web.security.client.realm.DefaultAuthorizingRealm.doGetAuthorizationInfo(PrincipalCollection),這樣就繞過了緩存,總是取我們自己最新的權(quán)限緩存Object key
= getAuthorizationCacheKey(principals);info = cache.get(key);if (log.isTraceEnabled()) {if (info == null) {log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");} else {log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");}}}if (info == null) {// Call template method if the info was not found in a cacheinfo = doGetAuthorizationInfo(principals);// If the info is not null and the cache has been created, then cache the authorization info.if (info != null && cache != null) {if (log.isTraceEnabled()) {log.trace("Caching authorization info for principals: [" + principals + "].");}Object key = getAuthorizationCacheKey(principals);cache.put(key, info);}}return info;}

這種方式還有一個(gè)缺陷就是會(huì)導(dǎo)致rpc較多,后面只要實(shí)現(xiàn)自己的CacheManager引用本地,然后監(jiān)聽基礎(chǔ)應(yīng)用的Logout事件去更新即可,后面再講。

Shiro包含的標(biāo)簽? ?

?guest標(biāo)簽:驗(yàn)證當(dāng)前用戶是否為“訪客”,即未認(rèn)證(包含未記住)的用戶;shiro標(biāo)簽:<shiro:guest></shiro:guest> ?;freemark中: <@shiro.guest> ?</@shiro.guest>?
? ??user標(biāo)簽:認(rèn)證通過或已記住的用戶 shiro標(biāo)簽:<shiro:user>?</shiro:user> ?;freemark中:?<@shiro.user>?</@shiro.user>?
? ??authenticated標(biāo)簽:已認(rèn)證通過的用戶。不包含已記住的用戶,這是與user標(biāo)簽的區(qū)別所在。 shiro標(biāo)簽:<shiro:authenticated>?</shiro:authenticated>;freemark中:?<@shiro.authenticated></@shiro.authenticated>
? ??notAuthenticated標(biāo)簽:未認(rèn)證通過的用戶。與authenticated標(biāo)簽相對(duì)。 shiro標(biāo)簽:<shiro:notAuthenticated>?</shiro:notAuthenticated>;freemark中:?<@shiro.notAuthenticated></@shiro.notAuthenticated>
? ??principal標(biāo)簽:輸出當(dāng)前用戶信息,通常為登錄帳號(hào)信息 ?shiro標(biāo)簽:Hello, ?<@shiro.principal property="name" /> ?;freemarker中:??Hello, ?<@shiro.principal property="name" />, how are you today? ?? ?
? ??hasRole標(biāo)簽:驗(yàn)證當(dāng)前用戶是否屬于該角色 ,shiro標(biāo)簽:?<shiro:hasRole name="administrator">??Administer the system?</shiro:hasRole> ;freemarker中:<@shiro.hasRole name=”admin”>Hello admin!</@shiro.hasRole>?
? ??hasAnyRoles標(biāo)簽:驗(yàn)證當(dāng)前用戶是否屬于這些角色中的任何一個(gè),角色之間逗號(hào)分隔 ,shiro標(biāo)簽:?<shiro:hasAnyRoles name="admin,user,operator">??Administer the system?</shiro:hasAnyRoles> ;freemarker中:<@shiro.hasAnyRoles name="admin,user,operator">Hello admin!</@shiro.hasAnyRoles>
? ??hasPermission標(biāo)簽:驗(yàn)證當(dāng)前用戶是否擁有該權(quán)限 ,shiro標(biāo)簽:?<shiro:hasPermission?name="/order:*">??訂單?</shiro:hasPermission> ;freemarker中:<@shiro.hasPermission?name="/order:*">訂單/@shiro.hasPermission> (一般來說,主要使用這個(gè))
? ??lacksRole標(biāo)簽:驗(yàn)證當(dāng)前用戶不屬于該角色,與hasRole標(biāo)簽想反,shiro標(biāo)簽:?<shiro:hasRole name="admin">??Administer the system?</shiro:hasRole> ;freemarker中:<@shiro.hasRole name="admin">Hello admin!</@shiro.hasRole>?
? ??lacksPermission標(biāo)簽:驗(yàn)證當(dāng)前用戶不擁有某種權(quán)限,與hasPermission標(biāo)簽是相對(duì)的,shiro標(biāo)簽:?<shiro:lacksPermission?name="/order:*">?trade?</shiro:lacksPermission> ;freemarker中:<@shiro.lacksPermission?name="/order:*">trade</@shiro.lacksPermission>?

其他

使用的環(huán)境為Spring MVC+FreeMarker,要在ftl頁面中使用contextPath,需要在viewResolver中做如下配置(紅色部分)

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="requestContextAttribute" value="rc"></property>
</bean>

這樣,在頁面中使用${rc.contextPath} 就可獲得contextPath。

org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method

沒有登錄,沒有認(rèn)證信息的原因。?

Shiro權(quán)限配置錯(cuò)誤There is no filter with name 'anno' to apply to chain

?org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroFilter': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: There is no filter with name 'anno' to apply to chain [/preLogin] in the pool of available Filters. Ensure a filter with that name/path has first been registered with the addFilter method(s).

有可能是順序的問題(至少筆者碰到的是這樣),先配置anno即可,如下:

<!-- 正確,沒有問題的 --><bean id="$user" class="com.hundsun.ta.web.security.client.filter.UserFilter" /><bean id="$authc" class="com.hundsun.ta.web.security.client.filter.FormAuthenticationFilter" /><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager" /><property name="loginUrl" value="/total-console.html" /><property name="successUrl" value="www.baidu.com"/><property name="filterChainDefinitions"><value>/ = anon/login.html = $authc/static/** = anon/** = $user</value></property></bean> <!-- 異常報(bào)錯(cuò)的 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager" /><property name="loginUrl" value="/total-console.html" /><property name="successUrl" value="www.baidu.com"/><property name="filterChainDefinitions"><value>/ = anon/login.html = $authc/static/** = anon/** = $user</value></property></bean><bean id="$user" class="com.hundsun.ta.web.security.client.filter.UserFilter" /><bean id="$authc" class="com.hundsun.ta.web.security.client.filter.FormAuthenticationFilter" />

shiro集成進(jìn)來后,調(diào)用API直接404異常

@GetMapping("/user")@RequiresPermissions(value={"user:add","resource:delete"},logical = Logical.OR)public User getUserInfo(@RequestParam(value = "crsKey") String username){return userService.findByUsername(username);}

如果把RequiresPermissions這行去掉,是可以正常訪問的,加上之后就是404。?

解決方法:給DefaultAdvisorAutoProxyCreator加上usePrefix屬性即可,如下:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"><property name="usePrefix" value="true" /></bean>

?

java.lang.IllegalArgumentException: SessionContext must be an HTTP compatible implementation.
at org.apache.shiro.web.session.mgt.ServletContainerSessionManager.createSession(ServletContainerSessionManager.java:103) ~[shiro-web-1.3.2.jar:1.3.2]
at org.apache.shiro.web.session.mgt.ServletContainerSessionManager.start(ServletContainerSessionManager.java:64) ~[shiro-web-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.SessionsSecurityManager.start(SessionsSecurityManager.java:152) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.subject.support.DelegatingSubject.getSession(DelegatingSubject.java:336) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.subject.support.DelegatingSubject.getSession(DelegatingSubject.java:312) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSubjectDAO.mergePrincipals(DefaultSubjectDAO.java:204) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSubjectDAO.saveToSession(DefaultSubjectDAO.java:166) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSubjectDAO.save(DefaultSubjectDAO.java:147) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSecurityManager.save(DefaultSecurityManager.java:383) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSecurityManager.createSubject(DefaultSecurityManager.java:350) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSecurityManager.createSubject(DefaultSecurityManager.java:183) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:283) ~[shiro-core-1.3.2.jar:1.3.2]
at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:256) ~[shiro-core-1.3.2.jar:1.3.2]
at com.hundsun.ta.interceptor.SecurityInteceptor.preHandle(SecurityInteceptor.java:96) [classes/:?]
at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:133) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:962) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:469) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:392) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:311) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:395) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:254) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:349) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:175) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) [tomcat-embed-core-8.5.16.jar:8.5.16]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.16.jar:8.5.16]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_171]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_171]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.16.jar:8.5.16]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_171]

代碼如下:

SecurityUtils.setSecurityManager(SpringContextHolder.getBean(DefaultWebSecurityManager.class));//得到Subject及創(chuàng)建用戶名/密碼身份驗(yàn)證Token(即用戶身份/憑證) Subject subject = SecurityUtils.getSubject();if (!subject.isAuthenticated()) {logger.info(sessionBean.getId().toString() + "尚未登錄,開始自動(dòng)登錄!");UsernamePasswordToken token = new UsernamePasswordToken(sessionBean.getId().toString(),sessionBean.getPassword() == null ? "1" : sessionBean.getPassword()); //登錄密碼try {subject.login(token);} catch (Exception e) {logger.error("自動(dòng)登錄失敗!",e);}}

有時(shí)候會(huì)報(bào)錯(cuò),有時(shí)候不報(bào)錯(cuò),這就比較坑爹了,一開始沒有找到規(guī)律。經(jīng)過反復(fù)測(cè)試重現(xiàn)出來了,當(dāng)沒有權(quán)限拋出異常后系統(tǒng)會(huì)跳轉(zhuǎn)到error頁面,于是又進(jìn)入preHandler,再次去登錄,遂出現(xiàn)該問題,不是https://www.cnblogs.com/ningheshutong/p/6478080.html所述的問題,加上判斷如果是/error就不嘗試登錄,問題就解決,如下。

if (!path.equals("/error")) {SecurityUtils.setSecurityManager(SpringContextHolder.getBean("clientSecurityManager"));// 得到Subject及創(chuàng)建用戶名/密碼身份驗(yàn)證Token(即用戶身份/憑證)Subject subject = SecurityUtils.getSubject();if (!subject.isAuthenticated()) {logger.info(sessionBean.getId().toString() + "尚未登錄,開始自動(dòng)登錄!");UsernamePasswordToken token = new UsernamePasswordToken(sessionBean.getId().toString(),sessionBean.getPassword() == null ? "1" : sessionBean.getPassword()); // 登錄密碼try {subject.login(token);} catch (Exception e) {response.sendRedirect(appWebHomeUrl + "/logout.html");logger.error("自動(dòng)登錄失敗!", e);return false;}}}

使用這種方式還有一個(gè)注意點(diǎn),就是shiro的session超時(shí)時(shí)間設(shè)置,如下所示:

Shiro的Session接口有一個(gè)setTimeout()方法,登錄后,可以用如下方式取得session

SecurityUtils.getSubject().getSession().setTimeout(1800000);

設(shè)置的最大時(shí)間,正負(fù)都可以,為負(fù)數(shù)時(shí)表示永不超時(shí)。

SecurityUtils.getSubject().getSession().setTimeout(-1000l);

?默認(rèn)為1800秒。

參考:

https://blog.csdn.net/qq_26321411/article/details/79557264

https://blog.csdn.net/weixin_38132621/article/details/80216056

https://blog.csdn.net/u013615903/article/details/78781166/

http://shiro.apache.org/

https://www.infoq.com/minibooks/apache-shiro-ee-7

http://shiro.apache.org/webapp-tutorial.html

http://shiro.apache.org/java-authorization-guide.html

http://shiro.apache.org/java-authentication-guide.html

其他異常

在Springboot環(huán)境中繼承Shiro時(shí),使用注解@RequiresPermissions時(shí)無效,也就是似乎@RequestMapping失效了。解決方法: @Beanpublic DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true); -- 關(guān)鍵是要代理目標(biāo)類return advisorAutoProxyCreator;}

?

轉(zhuǎn)載于:https://www.cnblogs.com/zhjh256/p/10259479.html

總結(jié)

以上是生活随笔為你收集整理的springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。