Shiro框架:Shiro内置过滤器源码解析

目录

1. 常见项目中过滤器配置

2.Url访问控制配置解析为内置过滤器 

2.1 DefaultFilterChainManager构造并注册内置过滤器

2.2 构造过滤器链

3. Shiro内置过滤器解析

3.1 内置过滤器概览

3.2 公共继承类解析

3.2.1 顶层Filter接口

3.2.2 AbstractFilter

3.2.3 NameableFilter

3.2.4 OncePerRequestFilter

3.2.5 AdviceFilter

3.2.6 PathMatchingFilter

3.3 内置过滤器解析

3.3.1 AnonymousFilter

3.3.2 FormAuthenticationFilter

3.3.2.1 AccessControlFilter

3.3.2.2 AuthenticationFilter

3.3.2.3 AuthenticatingFilter

3.3.2.4 FormAuthenticationFilter处理逻辑

3.3.3 RolesAuthorizationFilter

3.3.3.1 AuthorizationFilter

 3.3.3.2 RolesAuthorizationFilter处理逻辑

3.3.4 PermissionsAuthorizationFilter

3.3.5 LogoutFilter


Shiro框架作为登录鉴权安全模块一款较为流行的开源框架,通过简单的配置即可完成登录鉴权配置,其中离不开Shiro较为丰富、且简单易用的内置过滤器,本文主要对Shiro多种内置过滤器进行源码解析,方便更好的深入透析其执行原理;

想要全面了解Shiro内置过滤器是如何通过Shiro自定义拦截器SpringShiroFilter进行注册构造的,可移步:Shiro框架:ShiroFilterFactoryBean过滤器源码解析-CSDN博客

1. 常见项目中过滤器配置

在Spring项目中应用Shiro进行登录鉴权安全模块开发配置时,我们会对指定的url配置访问控制策略,一种常见的url配置如下:

如上:

  • anno:配置不会被拦截的url
  • authc:配置必须认证通过才能访问的url
  • logout:配置logout拦截器,执行退出处理流程

Shiro对如上Url的过滤器配置是如何解析的呢,下面对该解析过程进行详细说明; 

2.Url访问控制配置解析为内置过滤器 

如上url过滤器配置添加到了ShiroFilterFactoryBean中,在ShiroFilterFactoryBean初始化bean的过程中,会对该过滤器配置进行解析;如下,其主要包含了以下几部分内容:

  • 构造DefaultFilterChainManager,注册内置过滤器并初始化
  • 自定义过滤器全局属性设置并注册
  • Url过滤链配置解析,构造过滤器链

下面进行详细解析;

2.1 DefaultFilterChainManager构造并注册内置过滤器

注册内置过滤器逻辑主要在DefaultFilterChainManager构造函数中,如下:

    public DefaultFilterChainManager() {this.filters = new LinkedHashMap<String, Filter>();this.filterChains = new LinkedHashMap<String, NamedFilterList>();addDefaultFilters(false);}protected void addDefaultFilters(boolean init) {for (DefaultFilter defaultFilter : DefaultFilter.values()) {addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);}}protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) {Filter existing = getFilter(name);if (existing == null || overwrite) {if (filter instanceof Nameable) {((Nameable) filter).setName(name);}if (init) {initFilter(filter);}this.filters.put(name, filter);}}

通过遍历所有内置过滤器DefaultFilter.values()并实例化,完成了内置过滤器的注册;

内置过滤器枚举如下:

2.2 构造过滤器链

在完成内置过滤器以及自定义过滤器(如有)注册到DefaultFilterChainManager之后,下面对url过滤链配置进行解析,主要是通过过滤器名称(比如anno、auchc)映射到已注册的过滤器,具体过程如下:

这里获取到前述已配置的url过滤器配置,并通过createChain方法构造过滤链,展开如下:

    public void createChain(String chainName, String chainDefinition) {String[] filterTokens = splitChainDefinition(chainDefinition);//each token is specific to each filter.//strip the name and extract any filter-specific config between brackets [ ]for (String token : filterTokens) {String[] nameConfigPair = toNameConfigPair(token);//now we have the filter name, path and (possibly null) path-specific config.  Let's apply them:addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);}}

nameConfigPair[0]即为解析到的过滤器名称,继续跟踪addToChain方法如下:

    public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {if (!StringUtils.hasText(chainName)) {throw new IllegalArgumentException("chainName cannot be null or empty.");}Filter filter = getFilter(filterName);if (filter == null) {throw new IllegalArgumentException("There is no filter with name '" + filterName +"' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " +"filter with that name/path has first been registered with the addFilter method(s).");}applyChainConfig(chainName, filter, chainSpecificFilterConfig);NamedFilterList chain = ensureChain(chainName);chain.add(filter);}public Filter getFilter(String name) {return this.filters.get(name);}protected NamedFilterList ensureChain(String chainName) {NamedFilterList chain = getChain(chainName);if (chain == null) {chain = new SimpleNamedFilterList(chainName);this.filterChains.put(chainName, chain);}return chain;}

如上,getFilter方法根据过滤器名称获取已注册的过滤器, ensureChain方法创建url对应的过滤器链,并添加上url映射到的过滤器;

至此,完成了Url访问控制配置解析为内置过滤器的解析; 

下面开始对本篇的核心内容-Shiro内置过滤器进行深入解析;

3. Shiro内置过滤器解析

3.1 内置过滤器概览

前述也给出了内置过滤器枚举的图示,如下:

其内部继承结构展示如下:

可以看到内置过滤器的种类以及类继承层次结构还是比较复杂的,我们已平时项目中较常用到的几种内置过滤器进行源码剖析,包括AnonymousFilter、FormAuthenticationFilter、RolesAuthorizationFilter、PermissionsAuthorizationFilter以及logout;

在具体分析内置过滤器之前,我们首先对继承结构中一些关键的类按照由上到下的顺序进行解析说明,方便更好的理解每个类继承层次的关键职责以及整体过滤器的处理逻辑,然后再对分别具体的内置过滤器进行解析;

3.2 公共继承类解析

3.2.1 顶层Filter接口

Servlet Filter接口,调用doFilter方法执行过滤逻辑:

    public void init(FilterConfig filterConfig) throws ServletException;public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException;public void destroy();

3.2.2 AbstractFilter

实现了Servlet Filter的初始化方法init,并定义了onFilterConfigSet的空实现:

    /*** Sets the filter's {@link #setFilterConfig filterConfig} and then immediately calls* {@link #onFilterConfigSet() onFilterConfigSet()} to trigger any processing a subclass might wish to perform.** @param filterConfig the servlet container supplied FilterConfig instance.* @throws javax.servlet.ServletException if {@link #onFilterConfigSet() onFilterConfigSet()} throws an Exception.*/public final void init(FilterConfig filterConfig) throws ServletException {setFilterConfig(filterConfig);try {onFilterConfigSet();} catch (Exception e) {if (e instanceof ServletException) {throw (ServletException) e;} else {if (log.isErrorEnabled()) {log.error("Unable to start Filter: [" + e.getMessage() + "].", e);}throw new ServletException(e);}}}protected void onFilterConfigSet() throws Exception {}

3.2.3 NameableFilter

见名知义,定义了过滤器的名称,以及名称的解析逻辑:

    /*** The name of this filter, unique within an application.*/private String name;/*** Returns the filter's name.* <p/>* Unless overridden by calling the {@link #setName(String) setName(String)} method, this value defaults to the* filter name as specified by the servlet container at start-up:* <pre>* this.name = {@link #getFilterConfig() getFilterConfig()}.{@link javax.servlet.FilterConfig#getFilterName() getName()};</pre>** @return the filter name, or {@code null} if none available* @see javax.servlet.GenericServlet#getServletName()* @see javax.servlet.FilterConfig#getFilterName()*/protected String getName() {if (this.name == null) {FilterConfig config = getFilterConfig();if (config != null) {this.name = config.getFilterName();}}return this.name;}

3.2.4 OncePerRequestFilter

实现了“每次请求、单次执行”的语义,内部是通过设置标志位属性的方式实现的,如下:

    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws ServletException, IOException {String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());filterChain.doFilter(request, response);} else //noinspection deprecationif (/* added in 1.2: */ !isEnabled(request, response) ||/* retain backwards compatibility: */ shouldNotFilter(request) ) {log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",getName());filterChain.doFilter(request, response);} else {// Do invoke this filter...log.trace("Filter '{}' not yet executed.  Executing now.", getName());request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);try {doFilterInternal(request, response, filterChain);} finally {// Once the request has finished, we're done and we don't// need to mark as 'already filtered' any more.request.removeAttribute(alreadyFilteredAttributeName);}}}/*** Same contract as for* {@link #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)},* but guaranteed to be invoked only once per request.** @param request  incoming {@code ServletRequest}* @param response outgoing {@code ServletResponse}* @param chain    the {@code FilterChain} to execute* @throws ServletException if there is a problem processing the request* @throws IOException      if there is an I/O problem processing the request*/protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException;

通过request中的属性Key:alreadyFilteredAttributeName是否已设置,判断是否已经执行;未执行则执行该过滤器逻辑,已执行则跳过,继续执行过滤链;

同时执行内部过滤逻辑委托给了抽象方法doFilterInternal,并交由子类来具体实现;

3.2.5 AdviceFilter

AdviceFilter继承自OncePerRequestFilter,并实现了抽象方法doFilterInternal,具体过滤逻辑如下:

   /*** Actually implements the chain execution logic, utilizing* {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) pre},* {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) post}, and* {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) after}* advice hooks.** @param request  the incoming ServletRequest* @param response the outgoing ServletResponse* @param chain    the filter chain to execute* @throws ServletException if a servlet-related error occurs* @throws IOException      if an IO error occurs*/public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException {Exception exception = null;try {boolean continueChain = preHandle(request, response);if (log.isTraceEnabled()) {log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");}if (continueChain) {executeChain(request, response, chain);}postHandle(request, response);if (log.isTraceEnabled()) {log.trace("Successfully invoked postHandle method");}} catch (Exception e) {exception = e;} finally {cleanup(request, response, exception);}}protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {chain.doFilter(request, response);}protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {return true;}protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {}public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {}

如上,AdviceFilter引入了扩展方法,并给出了默认空实现,也可交由子类进行方法覆盖:

preHandle:控制后续过滤链是否继续执行还是直接跳过

postHandle:handle后处理

afterCompletion:过滤链执行完成后的处理

这些扩展点实现了类似Aop模式的嵌入逻辑,并交由子类按需灵活实现;

3.2.6 PathMatchingFilter

继承AdviceFilter,实现了preHandle方法,执行url路径匹配,这里的appliedPaths即为url访问控制过滤器配置的url,路径匹配的条件下才执行过滤逻辑;

    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {if (log.isTraceEnabled()) {log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");}return true;}for (String path : this.appliedPaths.keySet()) {// If the path does match, then pass on to the subclass implementation for specific checks//(first match 'wins'):if (pathsMatch(path, request)) {log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);Object config = this.appliedPaths.get(path);return isFilterChainContinued(request, response, path, config);}}//no path matched, allow the request to go through:return true;}

 isFilterChainContinued方法实现如下,内部判断过滤器是否开启,如果已开启,引入了新的扩展方法onPreHandle来判断是否执行该过滤链:

    private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,String path, Object pathConfig) throws Exception {if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2if (log.isTraceEnabled()) {log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}].  " +"Delegating to subclass implementation for 'onPreHandle' check.",new Object[]{getName(), path, pathConfig});}//The filter is enabled for this specific request, so delegate to subclass implementations//so they can decide if the request should continue through the chain or not:return onPreHandle(request, response, pathConfig);}if (log.isTraceEnabled()) {log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}].  " +"The next element in the FilterChain will be called immediately.",new Object[]{getName(), path, pathConfig});}//This filter is disabled for this specific request,//return 'true' immediately to indicate that the filter will not process the request//and let the request/response to continue through the filter chain:return true;}
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {return true;}

onPreHandle方法默认返回true,表示继续执行后续过滤链,并可交由子类进行覆盖实现; 

3.3 内置过滤器解析

3.3.1 AnonymousFilter

AnonymousFilter,枚举名称为“anno”,表示不对url进行拦截,直接放行,其具体类图如下:

AnonymousFilter类定义非常简单,它继承自PathMatchingFilter,同时覆盖了扩展方法onPreHandle,并固定返回true,表示继续执行后续过滤链,直接放行;

AnonymousFilter类定义如下:

public class AnonymousFilter extends PathMatchingFilter {/*** Always returns <code>true</code> allowing unchecked access to the underlying path or resource.** @return <code>true</code> always, allowing unchecked access to the underlying path or resource.*/@Overrideprotected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {// Always return true since we allow access to anyonereturn true;}}

3.3.2 FormAuthenticationFilter

FormAuthenticationFilter过滤器继承层次结构如下:

这里也是继承自PathMatchingFilter,下面对继承结构中的其它几个类进行具体分析;

3.3.2.1 AccessControlFilter

访问控制过滤器,内部覆盖实现了onPreHandle方法,允许放行(即返回true)的条件是:

  1. 已认证通过,比如用户已登录;
  2. 未认证通过时,也即访问拒绝后,依据访问拒绝后的操作结果来判断是否继续执行后续过滤链,比如访问拒绝后会执行用户登录,如果登录成功,则继续执行后续过滤链
    /*** Returns <code>true</code> if* {@link #isAccessAllowed(ServletRequest,ServletResponse,Object) isAccessAllowed(Request,Response,Object)},* otherwise returns the result of* {@link #onAccessDenied(ServletRequest,ServletResponse,Object) onAccessDenied(Request,Response,Object)}.** @return <code>true</code> if*         {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed},*         otherwise returns the result of*         {@link #onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse) onAccessDenied}.* @throws Exception if an error occurs.*/public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);}

同时,AccessControlFilter又引入了新的扩展方法交由子类灵活实现,如下:

    /*** Returns <code>true</code> if the request is allowed to proceed through the filter normally, or <code>false</code>* if the request should be handled by the* {@link #onAccessDenied(ServletRequest,ServletResponse,Object) onAccessDenied(request,response,mappedValue)}* method instead.** @param request     the incoming <code>ServletRequest</code>* @param response    the outgoing <code>ServletResponse</code>* @param mappedValue the filter-specific config value mapped to this filter in the URL rules mappings.* @return <code>true</code> if the request should proceed through the filter normally, <code>false</code> if the*         request should be processed by this filter's*         {@link #onAccessDenied(ServletRequest,ServletResponse,Object)} method instead.* @throws Exception if an error occurs during processing.*/protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;/*** Processes requests where the subject was denied access as determined by the* {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed}* method, retaining the {@code mappedValue} that was used during configuration.* <p/>* This method immediately delegates to {@link #onAccessDenied(ServletRequest,ServletResponse)} as a* convenience in that most post-denial behavior does not need the mapped config again.** @param request     the incoming <code>ServletRequest</code>* @param response    the outgoing <code>ServletResponse</code>* @param mappedValue the config specified for the filter in the matching request's filter chain.* @return <code>true</code> if the request should continue to be processed; false if the subclass will*         handle/render the response directly.* @throws Exception if there is an error processing the request.* @since 1.0*/protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {return onAccessDenied(request, response);}/*** Processes requests where the subject was denied access as determined by the* {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed}* method.** @param request  the incoming <code>ServletRequest</code>* @param response the outgoing <code>ServletResponse</code>* @return <code>true</code> if the request should continue to be processed; false if the subclass will*         handle/render the response directly.* @throws Exception if there is an error processing the request.*/protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;
3.3.2.2 AuthenticationFilter

AuthenticationFilter实现了isAccessAllowed方法,判断当前登录用户是否已认证通过:

    /*** Determines whether the current subject is authenticated.* <p/>* The default implementation {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) acquires}* the currently executing Subject and then returns* {@link org.apache.shiro.subject.Subject#isAuthenticated() subject.isAuthenticated()};** @return true if the subject is authenticated; false if the subject is unauthenticated*/protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {Subject subject = getSubject(request, response);return subject.isAuthenticated();}
3.3.2.3 AuthenticatingFilter

覆盖实现了isAccessAllowed方法,判断是否允许访问,允许访问的条件包括:

  1. 用户已认证
  2. 非登录url且url路径配置中包括“permissive”,表示允许放行
    /*** Determines whether the current subject should be allowed to make the current request.* <p/>* The default implementation returns <code>true</code> if the user is authenticated.  Will also return* <code>true</code> if the {@link #isLoginRequest} returns false and the &quot;permissive&quot; flag is set.** @return <code>true</code> if request should be allowed access*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return super.isAccessAllowed(request, response, mappedValue) ||(!isLoginRequest(request, response) && isPermissive(mappedValue));}/*** Returns <code>true</code> if the mappedValue contains the {@link #PERMISSIVE} qualifier.** @return <code>true</code> if this filter should be permissive*/protected boolean isPermissive(Object mappedValue) {if(mappedValue != null) {String[] values = (String[]) mappedValue;return Arrays.binarySearch(values, PERMISSIVE) >= 0;}return false;}
3.3.2.4 FormAuthenticationFilter处理逻辑

上述涉及的父类解析完毕,我们看下FormAuthenticationFilter本身的主要实现逻辑;

FormAuthenticationFilter覆盖了AccessControlFilter类提供的扩展方法onAccessDenied,执行访问拒绝后的后续操作,具体实现如下:

可以看到这里主要包含了2个代码分支:

  1. 如果提交url是登录url且是登录提交,则会执行具体的登录操作
  2. 如果不是登录url,又因为之前还未认证通过,则重定向到登录页面,引导用户登录
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {if (isLoginRequest(request, response)) {if (isLoginSubmission(request, response)) {if (log.isTraceEnabled()) {log.trace("Login submission detected.  Attempting to execute login.");}return executeLogin(request, response);} else {if (log.isTraceEnabled()) {log.trace("Login page view.");}//allow them to see the login page ;)return true;}} else {if (log.isTraceEnabled()) {log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +"Authentication url [" + getLoginUrl() + "]");}saveRequestAndRedirectToLogin(request, response);return false;}}

executeLogin方法实现了具体的登录操作,获取当面登录用户,然后执行登录login;

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {AuthenticationToken token = createToken(request, response);if (token == null) {String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +"must be created in order to execute a login attempt.";throw new IllegalStateException(msg);}try {Subject subject = getSubject(request, response);subject.login(token);return onLoginSuccess(token, subject, request, response);} catch (AuthenticationException e) {return onLoginFailure(token, e, request, response);}}

登录成功,会重定向到成功页面,并不再执行后续过滤链;

登录失败,设置登录失败属性,用户可以继续登录;

    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,ServletRequest request, ServletResponse response) throws Exception {issueSuccessRedirect(request, response);//we handled the success redirect directly, prevent the chain from continuing:return false;}protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,ServletRequest request, ServletResponse response) {if (log.isDebugEnabled()) {log.debug( "Authentication exception", e );}setFailureAttribute(request, e);//login failed, let request continue back to the login page:return true;}protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {String className = ae.getClass().getName();request.setAttribute(getFailureKeyAttribute(), className);}

3.3.3 RolesAuthorizationFilter

RolesAuthorizationFilter类继承层次结构如下,继承了AuthorizationFilter抽象类,进一步继承了AccessControlFilter,下面首先看下AuthorizationFilter的实现;

3.3.3.1 AuthorizationFilter

AuthorizationFilter内部覆盖了onAccessDenied方法,如下:

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {Subject subject = getSubject(request, response);// If the subject isn't identified, redirect to login URLif (subject.getPrincipal() == null) {saveRequestAndRedirectToLogin(request, response);} else {// If subject is known but not authorized, redirect to the unauthorized URL if there is one// If no unauthorized URL is specified, just return an unauthorized HTTP status codeString unauthorizedUrl = getUnauthorizedUrl();//SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:if (StringUtils.hasText(unauthorizedUrl)) {WebUtils.issueRedirect(request, response, unauthorizedUrl);} else {WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);}}return false;}

在没有访问控制权限的情况下,会重定向到无权限的页面,提示用户无访问权限,并返回false终止后续过滤链的执行;

 3.3.3.2 RolesAuthorizationFilter处理逻辑

内部实现覆盖了isAccessAllowed方法,如下:

主要逻辑是判断当前登录用户是否具有配置的角色(Role),配置角色支持使用注解RequiresRoles进行定义;

/*** Filter that allows access if the current user has the roles specified by the mapped value, or denies access* if the user does not have all of the roles specified.** @since 0.9*/
public class RolesAuthorizationFilter extends AuthorizationFilter {//TODO - complete JavaDoc@SuppressWarnings({"unchecked"})public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {Subject subject = getSubject(request, response);String[] rolesArray = (String[]) mappedValue;if (rolesArray == null || rolesArray.length == 0) {//no roles specified, so nothing to check - allow access.return true;}Set<String> roles = CollectionUtils.asSet(rolesArray);return subject.hasAllRoles(roles);}}

3.3.4 PermissionsAuthorizationFilter

PermissionsAuthorizationFilter和RolesAuthorizationFilter一样,同样继承AuthorizationFilter类,且覆盖实现了isAccessAllowed方法,如下:

校验用户是否有指定权限,支持注解RequiresPermissions进行权限配置;

/*** Filter that allows access if the current user has the roles specified by the mapped value, or denies access* if the user does not have all of the roles specified.** @since 0.9*/
public class RolesAuthorizationFilter extends AuthorizationFilter {//TODO - complete JavaDoc@SuppressWarnings({"unchecked"})public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {Subject subject = getSubject(request, response);String[] rolesArray = (String[]) mappedValue;if (rolesArray == null || rolesArray.length == 0) {//no roles specified, so nothing to check - allow access.return true;}Set<String> roles = CollectionUtils.asSet(rolesArray);return subject.hasAllRoles(roles);}}

3.3.5 LogoutFilter

登录过滤器LogoutFilter,继承层次结构如下:

其也是继承自AdviceFilter,并覆盖实现了preHandle方法,如下:

    @Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {Subject subject = getSubject(request, response);// Check if POST only logout is enabledif (isPostOnlyLogout()) {// check if the current request's method is a POST, if not redirectif (!WebUtils.toHttp(request).getMethod().toUpperCase(Locale.ENGLISH).equals("POST")) {return onLogoutRequestNotAPost(request, response);}}String redirectUrl = getRedirectUrl(request, response, subject);//try/catch added for SHIRO-298:try {subject.logout();} catch (SessionException ise) {log.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);}issueRedirect(request, response, redirectUrl);return false;}

如上内部实现获取了当前登录用户,并执行用户登出操作,并引导跳转到重定向url;

这里logout的具体实现以及上面用户登录、角色控制、权限控制的具体操作,都是委托SecurityManager安全管理器来完成的,由于篇幅较长,不再展开说明,待后续再单独进行深入分析;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二十几种未授权访问漏洞合集

未授权访问漏洞是一个在企业内部非常常见的问题&#xff0c;这种问题通常都是由于安全配置不当、认证页面存在缺陷&#xff0c;或者压根就没有认证导致的。当某企业对外的服务端口、功能无限制开放&#xff0c;并且对用户的访问没有做任何限制的时候&#xff0c;可能会泄露出某…

PLC数组队列搜索FC(SCL代码+梯形图程序)

根据输入数据搜索输入数据队列中和输入数据相同的数,函数返回其所在队列的位置。这里我们需要用到博途PLC的数组指针功能,有关数组指针的详细使用方法,可以参考下面文章: 博途PLC数组指针: https://rxxw-control.blog.csdn.net/article/details/134761364 区间搜索FC …

常用计算电磁学算法特性与电磁软件分析

常用计算电磁学算法特性与电磁软件分析 参考网站&#xff1a; 计算电磁学三大数值算法FDTD、FEM、MOM ADS、HFSS、CST 优缺点和应用范围详细教程 ## 基于时域有限差分法的FDTD的计算电磁学算法&#xff08;含Matlab代码&#xff09;-框架介绍 参考书籍&#xff1a;The finite…

【python】06.函数和模块的使用

函数和模块的使用 在讲解本章节的内容之前&#xff0c;我们先来研究一道数学题&#xff0c;请说出下面的方程有多少组正整数解。 事实上&#xff0c;上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。 可以用Python的程序来…

Spring Boot 整合支付宝实现在线支付方案(沙箱环境)

文章目录 1.理解沙箱环境2.沙箱环境接入准备2.1 访问开发者控制台2.2 获取重要信息2.3 处理秘钥 3.接入支付宝支付的流程4.实现支付4.1 添加 SDK 依赖4.2 创建配置类4.3 支付宝订单管理接口实现流程4.4 支付宝支付接口实现流程 5.支付宝支付功能演示7.总结 TIP&#xff1a;对于…

【UEFI基础】EDK网络框架(VLAN)

VLAN VLAN代码综述 在MNP中有很多的VLAN介绍&#xff0c;MNP存在的一个重要原因也是为了处理VLAN&#xff0c;而本文介绍的NetworkPkg\VlanConfigDxe\VlanConfigDxe.inf其实只是一个帮助模块&#xff0c;真正的VLAN配置还是在MNP中。 VLAN同样是一个UEFI Driver Model&#…

Redis实现分布式会话

Redis实现分布式会话 1 什么是分布式会话 1 这是我么之前学过的注册登录模式 2 如果非常多的人访问&#xff0c;因为单台服务器的访问承受能力是有限的&#xff0c;那么我们就想用多态服务器来承担压力 3 一般通过负载均衡的方式来实现&#xff0c;来分担服务器的压力。 4 负…

【PlantUML】- 时序图

写在前面 本篇文章&#xff0c;我们来介绍一下PlantUML的时序图。这个相对类图来讲&#xff0c;比较简单&#xff0c;也不需要布局。读完文章&#xff0c;相信你就能实际操作了。 目录 写在前面一、基本概念二、具体步骤1.环境说明2.元素3.语法4.示例 三、参考资料写在后面系列…

软件测试|Python数据可视化神器——pyecharts教程(十)

使用pyecharts绘制漏斗图 简介 漏斗图&#xff08;Funnel Chart&#xff09;是一种用于可视化数据流程或转化率的图表类型。它通常由一系列阶段组成&#xff0c;每个阶段都有一个名称和一个值&#xff0c;表示在该阶段的转化量或数据流程的进展情况。漏斗图的名称来源于其外观…

Web自动化测试,一定得掌握的 8 个核心知识点

使用 cypress 进行端对端测试&#xff0c;和其他的一些框架有一个显著不同的地方&#xff0c;它使用 javascript 作为编程语言。传统主流的 selenium 框架是支持多语言的&#xff0c;大多数 QA 会的python 和 java 语言都可以编写 selenium 代码&#xff0c;遇到需要编写 js 代…

好用的便签有哪些?windows便签工具在哪打开?

每当我8点准时上班&#xff0c;在等待电脑开机的过程&#xff0c;我都会习惯性地思考整理今天要晚上的任务&#xff0c;列出所要完成的待办事项。随着每一项任务的清晰呈现&#xff0c;我的心情也逐渐明朗起来。当然了&#xff0c;这个时候&#xff0c;我迫切需要一款好用的便签…

VS游戏打包教程

我用得天天酷跑小游戏做的例子 1:安装打包插件 2:在解决方案里新建一个项目 3:新建一个setup项目 4:界面如下(通过右键folder,可以创建folder目录和输出) 5:素材文件 6:素材放完了就项目输出 7:创建快捷方式 右键这个主输出选择第一个create shortcut 8:将这个快捷方式,拖到,…

算法通关村番外篇-LeetCode编程从0到1系列一

大家好我是苏麟 , 今天开始带来LeetCode编程从0到1系列 . 编程基础 0 到 1 , 50 题掌握基础编程能力 大纲 1768.交替合并字符串389. 找不同28. 找出字符串中第一个匹配项的下标242. 有效的字母异位词459. 重复的子字符串283. 移动零66. 加一1822. 数组元素积的符号1502. 判断能…

Canopen学习笔记——sync同步报文增加数据域(同步计数器)

1.Canfestival同步报文sync的设置 在OD表中的配置如下&#xff1a; 如果0x1006索引的同步报文循环周期时间设置为0则禁用同步报文&#xff0c;这里要注意的就是&#xff0c;上面第一张图也提到了&#xff0c;时间单位是us。第二张图&#xff0c;我的0x1006就设置为0xF4240,也就…

Java面试之虚拟机

1、前言 本篇的面试题基于网络整理&#xff0c;和自己编辑。在不断的完善补充哦。 2、什么是虚拟机&#xff1f; Java 虚拟机&#xff0c;是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件( .class )。 Java 被设计成允许应用程…

LeetCode讲解篇之90. 子集 II

文章目录 题目描述题解思路题解代码 题目描述 题解思路 初始化一个变量start表示当前从哪里开始遍历nums 搜索过程的数字组合加入结果集 从start开始遍历nums 如果当前元素和前一个元素相等&#xff0c;前一个元素没被使用&#xff0c;则触发剪枝去重操作&#xff0c;跳过当…

58.leetcode 最后一个单词的长度

一、题目 二、解答 1. 思路 分2种情况 第一种情况只有一个单词&#xff0c;不包含空格&#xff1a;这种情况直接返回单词本身的长度。第二种情况包含空格&#xff1a;先去掉首尾的空格&#xff0c;根据空格切割字符串生成一个字符串列表&#xff0c;返回倒数第一个索引位置字…

QT周五作业

题目&#xff1a;实现简单水果的价格重量计算 点击一次水果重量1 自动计算总价 代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QListWidgetItem> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAME…

HarmonyOS@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

Observed装饰器和ObjectLink装饰器&#xff1a;嵌套类对象属性变化 上文所述的装饰器仅能观察到第一层的变化&#xff0c;但是在实际应用开发中&#xff0c;应用会根据开发需要&#xff0c;封装自己的数据模型。对于多层嵌套的情况&#xff0c;比如二维数组&#xff0c;或者数…

每日算法打卡:蚂蚁感冒 day 13

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例1&#xff1a;输出样例1&#xff1a;输入样例2&#xff1a;输出样例2&#xff1a; 题目分析示例代码 原题链接 1211. 蚂蚁感冒 题目难度&#xff1a;简单 题目来源&#xff1a;第五届蓝桥杯省赛C A/B组 题目描述…