一个关于shiro的编程实践

shiro的demo很多,我也看了很多,基本掌握了用法,而去基于shiro做一部分按需定制的开发还是感觉有些难度.

这个例子是这样的:

1.会员登录前端网站,需按照其等级分配权限,查看网站上的数据(低等级不展示具体数值,反之)

2.管理员等角色登录网站后端,进行后端权限操作分配.(除了样式没设置,其它的都还可以了)

我认为的改变是从ShiroConfiguration这个配置类着手,code 如下

/**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     * <p>
     * Filter Chain定义说明
     * 1、一个URL可以配置多个Filter,使用逗号分隔
     * 2、当设置多个过滤器时,全部验证通过,才视为通过
     * 3、部分过滤器可指定参数,如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //TODO 2019-1-29 17:40
        //将设置url的参数改为application.properties中去配置
        //securityManager改为可选择的进行传入
        //customisedFilter的hashmap传入的url及URLPath...Filter改为参数可传入
        //customisedFilter这个HashMap装配了登录及登录后可未授权页面(admin的配置admin的,member的配置member的)
        //filterChainDefinitionMap
        
        //1.securityManager中的realm按需配置
        //2.shiroFilterFactoryBean中的set...Url为按需配置
        //3.shiroFilterFactoryBean.setFilters(customisedFilter);中的customisedFilter按需配置
        //**customisedFilter为put了仅仅一个"url",value为URL路径匹配过滤器(按需传入)
        //4.shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        //**filterChainDefinitionMap为按需配置



        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/admin/login"); //原为/login
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/admin/"); //原为/index
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/unauthorized"); //原为/unauthorized
        //拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //自定义拦截器
        Map<String, Filter> customisedFilter = new HashMap<>();
        customisedFilter.put("url", getURLPathMatchingFilter());

        //配置映射关系 anon表示不需要权限即可访问
        filterChainDefinitionMap.put("/admin/login", "anon");
//        filterChainDefinitionMap.put("/admin/index", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/admin/config/**", "anon");
        filterChainDefinitionMap.put("/admin/doLogout", "logout");
        ;//后台登出
//      单独放入member的配置中  filterChainDefinitionMap.put("/member/doLogout","logout");//前端客户登出
        filterChainDefinitionMap.put("/admin/**", "url"); //路径/shiro_admin/**全部需要进行权限验证
        //其他资源都需要认证  authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
        filterChainDefinitionMap.put("/admin/**", "user");
        shiroFilterFactoryBean.setFilters(customisedFilter);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

要分别设定其权限及验证有github上的一些demo可以参考,但不同的是没有了这些路径拦截配置.

ShiroFilterFactoryBean中有些数值的getter setter从这里入手,并重写FilterChainManager应该可以完成路径的自由配置:

同理可得重写ShiroFilterFactoryBean也一样会设定自由路径的配置:

首先第一步,在重写的CustomShiroFilterFactoryBean中,我们要设置参数的可判定性,因为上方中的loginUrl...包括put到filterChainDefinitionMap中的路径等都是固定的,我们要根据判断的用户类型去配给不同的路径.(当然这样做后在实际应用场景中是否能奏效还待考量)

根据这种思路改变的MyRealm

package com.tansuo365.test1.realm;

import com.tansuo365.test1.bean.Member;
import com.tansuo365.test1.bean.User;
import com.tansuo365.test1.entity.MyLoginInstance;
import com.tansuo365.test1.service.*;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Set;

@Component
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private MemberService memberService;//member
    @Autowired
    private UserService userService;//user

    @Autowired
    private MroleService mroleService;//member
    @Autowired
    private RoleService roleService; //user

    @Autowired
    private MemberPermissionService memberPermissionService;//member
    @Autowired
    private PermissionService permissionService;//user

    /*获取授权权限信息*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = null;
        //获取当前登录类型
        String loginType = (String)SecurityUtils.getSubject().getSession().getAttribute("loginType");
        Set<String> permissions = null;
        Set<String> roles = null;

        // 能进入到这里,表示账号已经通过验证了
        String username = (String)principals.getPrimaryPrincipal();

        if(LoginEnum.ADMIN.toString().equals(loginType)){
            permissions = permissionService.listPermissions(username);
            roles = roleService.listRoleNames(username);
        }
        if(LoginEnum.MEMBER.toString().equals(loginType)){
//            Member member = memberService.getByName(username);
            // 通过service获取角色和权限
            permissions = memberPermissionService.listMemberPermissions(username);
            roles = mroleService.listMroleNames(username);
        }
        // 授权对象
        info = new SimpleAuthorizationInfo();
        // 把通过service获取到的角色和权限放进去
        info.setStringPermissions(permissions);
        info.setRoles(roles);
        return info;
    }

    /*获取身份验证信息*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        User user = null;//后台用户
        Member member = null;//前端会员
        MyLoginInstance instance = null;


        //1.把AuthenticationToken转换为CustomizedToken
        CustomizedToken customizedToken = (CustomizedToken)token;
        //2.从CustomizedToken中获取memberName
        String nameInInput = customizedToken.getUsername();
        //3.若用户不存在,抛出UnknownAccountException异常
        String loginType = customizedToken.getLoginType();
        if(LoginEnum.ADMIN.toString().equals(loginType)){
            user = userService.getByName(nameInInput);
            if(null == user){
                throw new UnknownAccountException("管理员不存在!");
            }
            instance = user;
        }
        if(LoginEnum.MEMBER.toString().equals(loginType)){
            member = memberService.getByName(nameInInput);
            if(null == member){
                throw new UnknownAccountException("会员不存在!");
            }
            instance = member;
        }

        //4.根据用户的情况,来构建AuthenticationInfo对象并返回
        //通常使用的实现类为SimpleAuthenticationInfo
        Object principal = instance.getInstanceName();
        Object credentials = instance.getInstancePassword();
        String salt = instance.getInstanceSalt();
        String realmName = getName();
        ByteSource credentialsSalt = ByteSource.Util.bytes(salt);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
        return info;
    }
}

MyLoginInstance是一个接口,只是将获取名称密码和salt进行了低耦合化:

package com.tansuo365.test1.entity;

public interface MyLoginInstance {

    String getInstanceName();

    String getInstancePassword();

    String getInstanceSalt();
}

在建立了一些不该存在的接口后,尝试着改变CustomShiroFilterFactoryBean

如我所想,实现同一个接口的实现类可能都被调用到了:

Parameter 0 of method shirFilter in com.tansuo365.test1.shiro.ShiroConfiguration required a single bean, but 2 were found:
	- memberServiceImpl: defined in file [D:\新建文件夹\test1\target\classes\com\tansuo365\test1\service\impl\MemberServiceImpl.class]
	- userServiceImpl: defined in file [D:\新建文件夹\test1\target\classes\com\tansuo365\test1\service\impl\UserServiceImpl.class]

所以还是去除这些多余的接口.使用老方法.

在postman中测试,admin路径可以直接调到/admin/login, 但在路径user时返回404,若直接访问/user/login则可以访问会员的登录.

这说明了在URLPathMatchingFilter中还需要根据不同的用户去获取.:

URLPathMatchingFilter中的一段:

// 如果没有登录,就跳转到登录页面 
if (!subject.isAuthenticated()) {
    WebUtils.issueRedirect(request, response, "/admin/login");
        return false;
}

而URLPathMatchingFilter又被ShiroConfiguration拦截器所装配:

//自定义拦截器
Map<String, Filter> customisedFilter = new HashMap<>();
customisedFilter.put("url", getURLPathMatchingFilter());

在URLPathMatchingFilter中:

package com.tansuo365.test1.filter;

import com.tansuo365.test1.service.PermissionService;
import com.tansuo365.test1.util.SpringContextUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;

/*URL路径匹配过滤器*/
public class URLPathMatchingFilter extends PathMatchingFilter {
	@Autowired
	private PermissionService permissionService;

	@Override
	protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
		if(null==permissionService)
			permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);

		String requestURI = getPathWithinApplication(request);
		System.out.println("requestURI:" + requestURI);

		Subject subject = SecurityUtils.getSubject();
		// 如果没有登录,就跳转到登录页面 --
		if (!subject.isAuthenticated()) {
			WebUtils.issueRedirect(request, response, "/admin/login");
			return false;
		}

		// 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
		System.out.println("permissionService:"+permissionService);
		boolean needInterceptor = permissionService.needInterceptor(requestURI);
		if (!needInterceptor) {
			return true;
		} else {
			boolean hasPermission = false;
			String userName = subject.getPrincipal().toString();
			Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
			for (String url : permissionUrls) {
				// 这就表示当前用户有这个权限
				if (url.equals(requestURI)) {
					hasPermission = true;
					break;
				}
			}

			if (hasPermission)
				return true;
			else {
				UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");

				subject.getSession().setAttribute("ex", ex);

				WebUtils.issueRedirect(request, response, "/admin/unauthorized");
				return false;
			}

		}

	}
}

设定的权限Service以及没有登录会跳转的路径,没有权限的提示路径都是固定的.

看张开涛大神的<跟我学Shiro>可知,在controller调用

currentUser.login(customizedToken);

后,其会自动委托给Security Manager,其负责真正的身份验证逻辑,它会委托给Authenticator进行身份验证.

接口Authenticator只有一个认证信息方法:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.shiro.authc;

/**
 * An Authenticator is responsible for authenticating accounts in an application.  It
 * is one of the primary entry points into the Shiro API.
 * <p/>
 * Although not a requirement, there is usually a single 'master' Authenticator configured for
 * an application.  Enabling Pluggable Authentication Module (PAM) behavior
 * (Two Phase Commit, etc.) is usually achieved by the single {@code Authenticator} coordinating
 * and interacting with an application-configured set of {@link org.apache.shiro.realm.Realm Realm}s.
 * <p/>
 * Note that most Shiro users will not interact with an {@code Authenticator} instance directly.
 * Shiro's default architecture is based on an overall {@code SecurityManager} which typically
 * wraps an {@code Authenticator} instance.
 *
 * @see org.apache.shiro.mgt.SecurityManager
 * @see AbstractAuthenticator AbstractAuthenticator
 * @see org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator
 * @since 0.1
 */
public interface Authenticator {

    /**
     * Authenticates a user based on the submitted {@code AuthenticationToken}.
     * <p/>
     * If the authentication is successful, an {@link AuthenticationInfo} instance is returned that represents the
     * user's account data relevant to Shiro.  This returned object is generally used in turn to construct a
     * {@code Subject} representing a more complete security-specific 'view' of an account that also allows access to
     * a {@code Session}.
     *
     * @param authenticationToken any representation of a user's principals and credentials submitted during an
     *                            authentication attempt.
     * @return the AuthenticationInfo representing the authenticating user's account data.
     * @throws AuthenticationException if there is any problem during the authentication process.
     *                                 See the specific exceptions listed below to as examples of what could happen
     *                                 in order to accurately handle these problems and to notify the user in an
     *                                 appropriate manner why the authentication attempt failed.  Realize an
     *                                 implementation of this interface may or may not throw those listed or may
     *                                 throw other AuthenticationExceptions, but the list shows the most common ones.
     * @see ExpiredCredentialsException
     * @see IncorrectCredentialsException
     * @see ExcessiveAttemptsException
     * @see LockedAccountException
     * @see ConcurrentAccessException
     * @see UnknownAccountException
     */
    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException;
}

在自我实现的MyRealm中,继承了AuthorizingRealm类,而这个也继承了抽象类:

abstract class AuthenticatingRealm

而其上面的类及接口也是实现了:

public interface Realm

而该接口中,也有这个方法:

AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

在接口Authenticator中的AuthenticationInfo返回类型的方法虽然名称为authenticate但是返回类型,传入参数类型个数,包括异常类型均是一样的,可以理解为同样的一个作用的接口都是传入AuthenticationToken,返回AuthenticationInfo.

Authenticator是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现.

Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认
ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;

Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返
回/抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进
行访问。

securityManager 会按照 realms 指定的顺序进行身份认证,也就是说当realm1不匹配时,则取判定后一个如realm2,

但我们的要求是前端用户权限是前端用户的realm,后端是后端的realm,应该互不干涉.

经过调整可以进行不同类型的判定

重写的MyFormAuthenticationFilter好像不太管用,虽然改了一些但是好像作用不是很大,不过扩展还是可以的.

其主要实现了将登录的form给进行一些抽象化的改变,比如原来shiro中没有loginType,而这里则可以进行设置,

如果将loginType(一个Enum枚举)列在类中自然可用,当然如果后台可以添加不同类型用户时,这个要存入数据库.

那么就可以在这里进行改善:

package com.tansuo365.test1.shiro.filter;

import com.tansuo365.test1.shiro.realm.MyAuthenticationToken;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 如果后端可以设定其它的用户种类,那么种类名称应该存入数据库中,而不是写死在枚举类里.
 * 那么就可以结合这个扩展类来进行种类的查询及比较.
 * 比如在STEP 1位置判定loginType是否与数据库设定的相关.
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {


//    public String loginType = null;

    protected MyAuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);

//        String rememberMeParam = getRememberMeParam();
        String loginType = request.getParameter("loginType");
        //STEP 1:
        System.err.println("loginType in MyForm:" + loginType);
        System.err.println("getRememberMeBool(request):" + getRememberMeBool(request));

        return new MyAuthenticationToken(username, password, getRememberMeBool(request) == null ? false : true, loginType);
    }


    protected String getRememberMeBool(ServletRequest request) {
        return WebUtils.getCleanParam(request, DEFAULT_REMEMBER_ME_PARAM);
    }
}
之后在ShiroConfiguration中进行注入:

    @Bean
    public FormAuthenticationFilter formAuthenticationFilter(){
        return new MyFormAuthenticationFilter();
    }
这个就略过了,因为现在不需要做用户种类添加的工作(如果涉及到特大型多用户网站,需要扩展性的添加角色,那么就可以将这个类进行扩展,比如LOL经常加入新英雄,要设定英雄是AD,AP还是辅助,那么就可以在这个种类中去添加了)

略过略过.

回归主题,虽然判定


MyFormAuthenticationFilter

在shiro中有个抽象类:PathMatchingFilter,其功能可以让继承的自定义类实现权限路径的拦截.

package com.tansuo365.test1.filter;

import com.tansuo365.test1.bean.MemberPermission;
import com.tansuo365.test1.service.MemberPermissionService;
import com.tansuo365.test1.service.PermissionService;
import com.tansuo365.test1.util.SpringContextUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;

/*URL路径匹配过滤器*/
public class URLPathMatchingFilter extends PathMatchingFilter {
    @Autowired
    private PermissionService permissionService; //admin
    @Autowired
    private MemberPermissionService memberPermissionService; //member

    /*前置处理器*/
    @Override
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
            return manyWayToLogin(request,response);
    }


    public boolean manyWayToLogin(ServletRequest request, ServletResponse response) throws Exception {
        String requestURI = getPathWithinApplication(request);
        System.out.println("requestURI:" + requestURI);
        Subject subject = SecurityUtils.getSubject();
        boolean st = requestURI.startsWith("/");
        boolean ed = requestURI.endsWith("/");
        String requestURIPure = null;
        boolean needInterceptor = false;
        if (st || ed) {
            requestURIPure = requestURI.replace("/", "");
        }
        System.out.println("requestURIPure"+requestURIPure);

        if (requestURIPure.contains("admin")) {
            if (!subject.isAuthenticated()) {
                WebUtils.issueRedirect(request, response, "/admin/login");
                return false;
            }
            if (null == permissionService) {
                permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
            }
            needInterceptor = permissionService.needInterceptor(requestURI);
        }
        if (requestURIPure.contains("user")) {
            if (!subject.isAuthenticated()) {
                WebUtils.issueRedirect(request, response, "/user/login");
                return false;
            }
            if (null == permissionService) {
                memberPermissionService = SpringContextUtils.getContext().getBean(MemberPermissionService.class);
            }
            needInterceptor = memberPermissionService.needInterceptor(requestURI);
        }

        return true;

//        if (!needInterceptor) {
//            return true;
//        } else {
//            boolean hasPermission = false;
//            String userName = subject.getPrincipal().toString();
//            Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
//            for (String url : permissionUrls) {
//                // 这就表示当前用户有这个权限
//                if (url.equals(requestURI)) {
//                    hasPermission = true;
//                    break;
//                }
//            }
////
//            if (hasPermission)
//                return true;
//            else {
//
//                if (requestURI.contains("admin")) {
//                    UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
//                    subject.getSession().setAttribute("ex", ex);
//                    WebUtils.issueRedirect(request, response, "/admin/unauthorized");
//                }
//                if (requestURI.contains("user")) {
//                    UnauthorizedException ex = new UnauthorizedException("当前会员没有访问路径 " + requestURI + " 的权限");
//                    subject.getSession().setAttribute("ex", ex);
//                    WebUtils.issueRedirect(request, response, "/user/unauthorized");
//                }
//
//
//            }
//            return false;
//        }

    }


}


//        // 如果没有登录,就跳转到登录页面 --
//        if (!subject.isAuthenticated()) {
//            if (requestURI.contains("admin")) {
//
//            }
//
//
//            return false;
//        }

// 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
//        System.out.println("permissionService:" + permissionService);
//        boolean needInterceptor = permissionService.needInterceptor(requestURI);
//        if (!needInterceptor) {
//            return true;
//        } else {
//            boolean hasPermission = false;
//            String userName = subject.getPrincipal().toString();
//            Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
//            for (String url : permissionUrls) {
//                // 这就表示当前用户有这个权限
//                if (url.equals(requestURI)) {
//                    hasPermission = true;
//                    break;
//                }
//            }
//
//            if (hasPermission)
//                return true;
//            else {
//
////                if (requestURI.contains("admin")) {
////                    UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
////                    subject.getSession().setAttribute("ex", ex);
////                    WebUtils.issueRedirect(request, response, "/admin/unauthorized");
////                }
////                if (requestURI.contains("user")) {
////                    UnauthorizedException ex = new UnauthorizedException("当前会员没有访问路径 " + requestURI + " 的权限");
////                    subject.getSession().setAttribute("ex", ex);
////                    WebUtils.issueRedirect(request, response, "/user/unauthorized");
////                }
//                UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
//                subject.getSession().setAttribute("ex", ex);
//                WebUtils.issueRedirect(request, response, "/admin/unauthorized");
//
//                return false;
//            }
//
//        }





 

比如上面这个继承类,当然现在可能不用这个了,要自己重写.

package com.tansuo365.test1.shiro.filter;

import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MemberFilter extends UserFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        WebUtils.issueRedirect(request,response,"/deniedMember");
        return false;
    }


}
package com.tansuo365.test1.shiro.filter;

import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class AdminFilter extends UserFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        WebUtils.issueRedirect(request,response,"/deniedAdmin");
        return false;
    }


}

重写了两种用户的UserFilter,测试否认通过路径.

UserFilter同时继承了AccessControlFilter

下面是UserFilter源码,有两个方法,一个是允许通过,一个是拒绝通过.

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.shiro.web.filter.authc;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;

/**
 * Filter that allows access to resources if the accessor is a known user, which is defined as
 * having a known principal.  This means that any user who is authenticated or remembered via a
 * 'remember me' feature will be allowed access from this filter.
 * <p/>
 * If the accessor is not a known user, then they will be redirected to the {@link #setLoginUrl(String) loginUrl}</p>
 *
 * @since 0.9
 */
public class UserFilter extends AccessControlFilter {

    /**
     * Returns <code>true</code> if the request is a
     * {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
     * if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
     * is not <code>null</code>, <code>false</code> otherwise.
     *
     * @return <code>true</code> if the request is a
     * {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
     * if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
     * is not <code>null</code>, <code>false</code> otherwise.
     */
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginRequest(request, response)) {
            return true;
        } else {
            Subject subject = getSubject(request, response);
            // If principal is not null, then the user is known and should be allowed access.
            return subject.getPrincipal() != null;
        }
    }

    /**
     * This default implementation simply calls
     * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse) saveRequestAndRedirectToLogin}
     * and then immediately returns <code>false</code>, thereby preventing the chain from continuing so the redirect may
     * execute.
     */
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        saveRequestAndRedirectToLogin(request, response);
        return false;
    }
}
 


之后如上图需要在ShiroConfig或ShiroConfiguration中设置这个路径到shiroFilterFactoryBean的setFilters(参数中去)

访问页面/admin 以及 /user 会走重写UserFilter的两个子类.

同时我在想,既然UserFilter也实现了PathMatchingFilter那么原来的URLPathMatchingFilter这个重写的类也基本可以用不到了.

在MemberFilter和AdminFilter中各自去定义就好了.

猜你喜欢

转载自blog.csdn.net/qq_33871647/article/details/86693467