• 秒极啊!手把手带你进行shiro授权拦截器的重写,学到了学到了


    shiro整合前后端分离的springboots,Vue项目真的是有很多大坑啊。

    今天我的主题是:如何设置shiro过滤器。

    遇到问题:我的项目是前后端分离的,shiro里面有一个shiroFilterFactoryBean.setUnauthorizedUrl(“你自己的url”);
    函数

    这是什么意思呢:这表示如果你访问了一个需要权限的url,但是目前你登陆的角色没有权限,那么页面默认跳转的地址。
    看着似乎是没啥毛病。
    但是!!!
    如果是前后端分离怎么办呢?后端shiro让项目跳向前端某个页面,这里是前后端分离式的啊!当前端的用户(没权限)发送了一个请求被shiro直接拦截了,前端一脸懵不知道发生什么问题了,所以傻傻的在控制台返回一段红色的跨域错误信息。
    那么我们应该怎么解决呢?
    你肯定会想那就让后端让前端跳转啊,这里我要说,既然是前后端分离的话,后端只能给前端发送信息让前端根据返回的信息自行处理。
    但是问题又来了,shiro这玩意直接把前端请求拦截了啥都不返回,啥都不说一声,搞的前端还误解是跨域问题。

    那么我们就应该去改写shiro的拦截器,让它处理方式更人性化。

    不废话了,开始主题

    开始改写拦截器:

    //package com.igeekhome.ccs.tool.config;  //写自己的当前目录
    
    import lombok.SneakyThrows;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
    import org.springframework.boot.configurationprocessor.json.JSONObject;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class MyPermsFilter extends AuthorizationFilter {
    
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
                throws Exception {
            System.out.println("isAccessDenied");
            Subject subject = getSubject(request, response);
            String[] rolesArray = (String[]) mappedValue;
            System.out.println("subject:"+subject.getPrincipal());
            if (rolesArray == null || rolesArray.length == 0) {
                //no roles specified, so nothing to check - allow access.
                return true;
            }
    
            for(int i=0;i<rolesArray.length;i++){
                if(subject.isPermitted(rolesArray[i])){
                    System.out.println("rolealist:"+rolesArray[i]);
                    return true;
                }
            }
            return false;
        }
    
        @SneakyThrows
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            System.out.println("onAccessDenied");
            Subject subject = getSubject(request, response);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            // If the subject isn't identified, redirect to login URL
            if (subject.getPrincipal() == null) {
    //            saveRequestAndRedirectToLogin(request, response);     //没登陆就进入重新登陆接口,这里前后端分离不需要
                System.out.println("没登陆");
                String objectStr= "{'name':'没登陆'}";
                JSONObject jsonObject=new JSONObject(objectStr);
                PrintWriter wirte = null;
                wirte = response.getWriter();
                wirte.print(jsonObject);
            } else {
                System.out.println("没权限");
                String objectStr= "{'name':'没权限'}";
                JSONObject jsonObject=new JSONObject(objectStr);
                PrintWriter wirte = null;
                wirte = response.getWriter();
                wirte.print(jsonObject);
    //            JSONObject json = new JSONObject();
    //            json.put("state","403");
    //            json.put("msg","登录已失效,请重新登录!");
    //            out.println(json);
    //            out.flush();
    //            out.close();
            }
    
            return false;
        }
    }
    
    

    首先看到

    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
    
    

    也就是第一个函数
    解释一下:这个函数每次shiro启动都会运行,看到参数ServletRequest request, ServletResponse response这两个参数是为当前用户身份确认起作用的,再看到Object mappedValue,它里面存在你之前shiro配置文件里存入的权限要求:

    我这里存了一个:

    filterChainDefinitionMap.put("/detail/getclass","perms[user:teacher]");
    
    

    所以mappedValue里存的应该是user:teacher,当然前提是把它转化为String

    再看到代码:

    这是看看你之前有没有存权限,如果没存的话(列表大小为0或列表为空)返回true,shiro就不拦你了。
    再往下看:

    这里subject是什么?你往上看找到:

    这个就是获取你当前登陆的用户。
    然后注意了!

     if(subject.isPermitted(rolesArray[i])){
                    System.out.println("rolealist:"+rolesArray[i]);
                    return true;
                }
    
    

    这是干嘛,这就是拿当前用户判断isPermitted(),当前用户有没有perms的内容,也就是判断subject里面有没有”user:teacher”权限,然后这里有个大坑,网上基本上所有教程代码是这样的:

    他们是hasRole,其实也没毛病,但是我之前设置的是

    perms所以要用isPermitted,你要发清楚你加的是什么权限。
    接着下一步:
    如果找到了返回true,没找到从for循环出来,直接返回false。
    false出现了,怎么办呢?这时候第二个函数

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) 
    
    

    知道你返回是false的话它就会运行,看红色框内

    这代表什么意思,当然是获取当前用户的username了也就是标识码了,这里不懂得说明shiro你还是没弄懂()因为之前的自定义Realm类用过了。
    如果subject.getPrincipal() == null说明没有用户名,意味着你还没有登陆呢,以前代码这里是一个跳转函数(让前端跳到登陆页面,但是我是前后端分离所以没用!),这里我改成了

    			String objectStr= "{'name':'没登陆'}";
                JSONObject jsonObject=new JSONObject(objectStr);
                PrintWriter wirte = null;
                wirte = response.getWriter();
                wirte.print(jsonObject);
    
    

    这是把自定义的objectStr 字符串转化为jsob格式,然后用PrintWriter返回给前端,这样一旦出错那么shiro除了拦截请求外还会给前端发一个 "{‘name’:‘没登陆’}“的json信息,这些前端就不懵了就不会说什么跨域问题了。而是得到了json数据。然后下面的else部分内容就不说了,和上面几乎一样给前端发送一个”{‘name’:‘没权限’}"的信息,这意味着你权限出错了,并且用户也登陆了,那么就只有一个可能导致第一个函数返回false,那就是你本来就没有权限!

    好了好了说了一大堆。

    老规矩,给那些赶时间的朋友们:

    快餐:

    总结:

    三个点:
    1.为什么要写拦截器,并且如何去重写拦截器,上面说了哦。
    2.拦截器文件第一个函数里是用subject.isPermitted(rolesArray[i])还是subject.hasRole(rolesArray[i])根据自己在之前设置信息判断。

    我用的perms所以用subject.isPermitted(rolesArray[i])

    3.就是这么向前端返回数据信息

    然后贴出四分代码:
    shiroConfig:

    package //自己的包;
    
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @Configuration
    public class shiroConfig {
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            shiroFilterFactoryBean.setLoginUrl("/index/login");
            shiroFilterFactoryBean.setSuccessUrl("/Station/noauth");
    //        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
             shiroFilterFactoryBean.setUnauthorizedUrl("/Station/noauth");
    //        shiroFilter.setLoginUrl("");//身份认证失败,则跳转到登录页面的配置 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
    //        shiroFilter.setSuccessUrl("");//登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
    //        shiroFilter.setUnauthorizedUrl("");//没有权限默认跳转的页面
    //        shiroFilter.setFilterChainDefinitions("");//filterChainDefinitions的配置顺序为自上而下,以最上面的为准
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
            filterChainDefinitionMap.put("/Station/**", "anon");
            filterChainDefinitionMap.put("/index/**", "anon");
    //        filterChainDefinitionMap.put("/detail/**", "anon");
    //        filterChainDefinitionMap.put("/detail/**", "anon");
    //        filterChainDefinitionMap.put("/Goods/**", "authc");
            //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
    //        filterChainDefinitionMap.put("/**", "authc");
            filterChainDefinitionMap.put("/detail/getclass","perms[user:teacher]");
    
            // 1、创建过滤器Map,用来装自定义过滤器
            LinkedHashMap<String, Filter> map = new LinkedHashMap<>();
    
            // 2、将自定义过滤器放入map中,如果实现了自定义授权过滤器,那就必须在这里注册,否则Shiro不会使用自定义的授权过滤器
            map.put("perms", new MyPermsFilter());
    
            // 3、将过滤器Ma绑定到shiroFilterFactoryBean上
            shiroFilterFactoryBean.setFilters(map);
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
        @Bean
        public SecurityManager securityManager(CustomRealm realm){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            defaultWebSecurityManager.setRealm(realm);
            return  defaultWebSecurityManager;
        }
        @Bean
        public CustomRealm customRealm() {
            CustomRealm customRealm = new CustomRealm();
            customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return customRealm;
        }
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            // 使用md5 算法进行加密
            hashedCredentialsMatcher.setHashAlgorithmName("md5");
            // 设置散列次数: 意为加密几次
            hashedCredentialsMatcher.setHashIterations(2);
    
            return hashedCredentialsMatcher;
        }
    }
    
    

    CustomRealm:

    package //自己的包;
    
    import com.igeekhome.ccs.biz.IndexBiz;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AccountException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.apache.tomcat.websocket.AuthenticationException;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class CustomRealm extends AuthorizingRealm {
    //    @Autowired
    //    private LoginService loginService;
        @Autowired
        IndexBiz indexBiz;
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            String username = (String) SecurityUtils.getSubject().getPrincipal();
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            Set<String> stringSet = new HashSet<>();
            if (indexBiz.getsatte(username).equals("老师")){
                System.out.println("老师");
                stringSet.add("user:teacher");
            }else {
                System.out.println("学生");
                stringSet.add("user:student");
            }
            info.setStringPermissions(stringSet);
            return info;
        }
    
        /**
         * 这里可以注入userService,为了方便演示,我就写死了帐号了密码
         * private UserService userService;
         * <p>
         * 获取即将需要认证的信息
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
            System.out.println("-------身份认证方法--------");
            String userName = (String) authenticationToken.getPrincipal();
            String userPwd = new String((char[]) authenticationToken.getCredentials());
            //根据用户名从数据库获取密码
            String password = indexBiz.getpassword(userName);
    
            if (indexBiz.getsatte(userName)==null) {
                throw new AccountException("用户名错误");
            }
    //        else if (!userPwd.equals(password)) {
    //            throw new AccountException("密码不正确");
    //        }
            String dbPwd = indexBiz.getpassword(userName);
            return new SimpleAuthenticationInfo(userName,dbPwd, ByteSource.Util.bytes(userName + "salt"),getName());
        }
    
    }
    
    

    MyPermsFilter:

    package //自己的包;
    
    import lombok.SneakyThrows;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
    import org.springframework.boot.configurationprocessor.json.JSONObject;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class MyPermsFilter extends AuthorizationFilter {
    
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
                throws Exception {
            System.out.println("isAccessDenied");
            Subject subject = getSubject(request, response);
            String[] rolesArray = (String[]) mappedValue;
            System.out.println("subject:"+subject.getPrincipal());
            if (rolesArray == null || rolesArray.length == 0) {
                //no roles specified, so nothing to check - allow access.
                return true;
            }
    
            for(int i=0;i<rolesArray.length;i++){
                if(subject.isPermitted(rolesArray[i])){//subject.hasRole(rolesArray[i])
                    System.out.println("rolealist:"+rolesArray[i]);
                    return true;
                }
            }
            return false;
        }
    
        @SneakyThrows
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            System.out.println("onAccessDenied");
            Subject subject = getSubject(request, response);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            // If the subject isn't identified, redirect to login URL
            if (subject.getPrincipal() == null) {
    //            saveRequestAndRedirectToLogin(request, response);     //没登陆就进入重新登陆接口,这里前后端分离不需要
                System.out.println("没登陆");
                String objectStr= "{'name':'没登陆'}";
                JSONObject jsonObject=new JSONObject(objectStr);
                PrintWriter wirte = null;
                wirte = response.getWriter();
                wirte.print(jsonObject);
            } else {
                System.out.println("没权限");
                String objectStr= "{'name':'没权限'}";
                JSONObject jsonObject=new JSONObject(objectStr);
                PrintWriter wirte = null;
                wirte = response.getWriter();
                wirte.print(jsonObject);
    //            JSONObject json = new JSONObject();
    //            json.put("state","403");
    //            json.put("msg","登录已失效,请重新登录!");
    //            out.println(json);
    //            out.flush();
    //            out.close();
            }
    
            return false;
        }
    
    }
    

    最后

    文章的最后给大家安利一个福利,关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

  • 相关阅读:
    很有意思的“老黄历”网站
    ubuntu
    getopt在Python中的使用
    系统变量TERM不知是用来干什么的?它的值有vt100,vt220等,这些值代表什么意思?
    >/dev/null 2>&1
    linux下常用的ftp服务器软件
    Windows环境下访问NFS
    linux iSCSI target配置全过程
    iSCSI target在安全方面相关设定
    folly学习心得
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14079420.html
Copyright © 2020-2023  润新知