shiro多用户授权验证?

题目描述

一个JavaWeb的项目,前后端分为member和admin进行登录,要分别拥有权限设定.
现在已经弄好了权限五表(总共10个表,member和admin各五张).
在配置不同用户登录时不使用一个路径登录,而是member通过/member/login, admin通过/admin/login.
本人对shiro不是很熟悉,接触了一两天,现在还在看zhangkaitao的shiro-example,
感觉其中的多Realm即ModularRealmAuthenticator类并不是我想的那种,其只能根据账号去寻找多个Realm.
看了一些csdn的博客也是各有各的写法,无非是在login页面增加字段loginType,再封装AuthenticationToken,
通过loginType来调用realm.但我的登录属于两个页面,还需要根据不同的用户类型来返回不同的路径拦截.

clipboard.png

除了对Realm进行判定,还需要返回不同的路径拦截器,这个还没有想法.
有大神提点一下可好...不然只能等自己解决后上传答案了.

题目来源及自己的思路

重写DefaultFilterChainManager或是PathMatchingFilter,根据用户的不同添加不同的拦截.
实例化ShiroFilterFactoryBean根据不同设置.

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

现在的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.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
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 javax.annotation.Resource;
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("管理员不存在!");
            }
        }
        if(LoginEnum.MEMBER.toString().equals(loginType)){
            member = memberService.getByName(nameInInput);
            if(null == member){
                throw new UnknownAccountException("会员不存在!");
            }
        }

        //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;
    }
}

但是问题是路径还没实现可调整化:
只有一个admin的:

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;
            }

        }

    }
}

ShiroConfiguration也是只有admin的:

package com.tansuo365.test1.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.tansuo365.test1.filter.URLPathMatchingFilter;
import com.tansuo365.test1.realm.AdminRealm;

import com.tansuo365.test1.realm.MyRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

//import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/*权限配置类*/
@Configuration
public class ShiroConfiguration {
//    @Autowired
//    private MemberShiroConfiguration memberShiroConfiguration;

    //用于thymeleaf模板使用shiro标签
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * 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;
    }

//    public

    public URLPathMatchingFilter getURLPathMatchingFilter() {
        return new URLPathMatchingFilter();
    }


    /*安全管理器*/
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置自定义realm.
        securityManager.setRealm(getMyRealm());

        //配置记住我
        securityManager.setRememberMeManager(rememberMeManager());

//        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }



//    @Bean
//    public AdminRealm getAdminRealm(){
//        AdminRealm adminRealm = new AdminRealm();
//        adminRealm.setCredentialsMatcher(hashedCredentialsMatcher());
//        return adminRealm;
//    }

    @Bean
    public MyRealm getMyRealm(){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myRealm;
    }

//    @Bean
//    public MemberRealm getMemberRealm(){
//        MemberRealm memberRealm = new MemberRealm();
//        memberRealm.setCredentialsMatcher(hashedCredentialsMatcher());
//        return memberRealm;
//    }

//    @Bean
//    public DatabaseRealm getDatabaseRealm() {
//        DatabaseRealm myShiroRealm = new DatabaseRealm();
//        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
//        return myShiroRealm;
//    }

//    @Bean
//    public MemberDatabaseRealm getMemberDatabaseRealm() {
//        MemberDatabaseRealm memberShiroRealm = new MemberDatabaseRealm();
//        memberShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
//        return memberShiroRealm;
//    }

    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * 所以我们需要修改下doGetAuthenticationInfo中的代码;
     * )
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));

        return hashedCredentialsMatcher;
    }

    /**
     * cookie对象,会话Cookie模板,默认为:JSESSIONID
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/admin/");
        simpleCookie.setMaxAge(60 * 60 * 24 * 30);//一个月cookie保留时间
        return simpleCookie;
    }

    /**
     * cookie管理对象,记住我功能,rememberMe管理器
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * FormAuthenticationFilter过滤器 过滤记住我
     *
     * @return
     */
    @Bean
    public FormAuthenticationFilter formAuthenticationFilter() {
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

你期待的结果是什么?实际看到的错误信息又是什么?

member登录会根据member的realm, admin会根据admin的
并且路径拦截(loginUrl,successUrl...)也会根据不同的进行设置.

阅读 3k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题