zoukankan      html  css  js  c++  java
  • Springboot集成权限管理框架apache shiro

    一、名词解释

    网上一大堆

    二、pom依赖

    <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-spring</artifactId>
          <version>1.2.2</version>
        </dependency>
        <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-ehcache</artifactId>
            <version>1.2.2</version>
        </dependency>

    encache可选,主要用于鉴权时的缓存

    三、shiroConfiguration

    shiro的配置主要是shiroFilter和securityManager的设置

    @Component
    public class ShiroConfiguration {
    
    @Bean
    public EhCacheManager ehCacheManager() { EhCacheManager manager = new EhCacheManager(); manager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); return manager; } @Resource private MyShiroRealm myShiroRealm; @Bean public SecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(myShiroRealm); defaultWebSecurityManager.setCacheManager(ehCacheManager()); return defaultWebSecurityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String,String> map = new LinkedHashMap<>(); Map<String,Filter> filterMap = new LinkedHashMap<>(); filterMap.put("authc",loginFilter()); filterMap.put("perms",myFilter()); shiroFilterFactoryBean.setFilters(filterMap); //map.put("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D","anon"); //map.put("/**","authc"); map.put("/RPC52CA3404FDADAB18F91E8210DFCE1522","perms[admin:test]"); map.put("/RPC66EED9EBACF5FB42B9AD9C069495587F","perms[test]"); map.put("/**","authc"); //map.put("/**","myfilter"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /*@Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); //myShiroRealm.setCacheManager(manager); return myShiroRealm; }*/ @Bean public LoginFilter loginFilter() { return new LoginFilter(); } @Bean public MyFilter myFilter() { return new MyFilter(); } }

    ehCahceManager是注册缓存管理器,MyShiroRealm是权限认证具体的实现,需要注册到securityManager中,shiroFilter中主要设置过滤器链。这里面我主要用到了两个过滤器,perms和authc。

    perms是给访问URL设置访问权限的,比如map.put("/RPC52CA3404FDADAB18F91E8210DFCE1522","perms[admin:test]"),key是访问的URL,value是设置的权限,必须是perms[]的形式。我将需要访问的接口URL放到数据库中,在初始化配置的时候通过读取数据库将每一个需要鉴权的接口进行权限配置。authc要求必须登录认证。

    由于我们是前后端分离,前端通过RPC接口访问后端服务,这块我就想当没有权限或者没有登录时,给前端返回不同的code,所以自己实现了两个filter,loginFilter替换原来的authc过滤器,myFilter替换原来的perms过滤器。

    四、MyFilter和LoginFilter的实现

    loginFilter继承AuthenticationFilter,myFilter继承PermissionsAuthorizationFilter

    public class LoginFilter extends AuthenticationFilter {
        @Override
        protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)  {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            Subject subject = getSubject(servletRequest,servletResponse);
            String path = request.getServletPath();
            System.out.println("path = " + path);
            if(path.equals("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D"))
            {
                return true;
            }
            if(subject.getPrincipals() != null)
            {
                return true;
            }
            return false;
        }
        /**
         * 会话超时或权限校验未通过的,统一返回401,由前端页面弹窗提示
         */
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
                throws IOException {
    
            ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_LOGIN));
        return false;

    }
    }

    首先会执行isAccessAllowed方法,我将登录的几个接口在这里进行排除,直接返回true,就是登录的接口不需要进行登录认证。当返回false时执行onAccessDenied方法,这里我直接通过响应流返回给前端json数据。

    public class ShiroUtil {
    
        /**
         * 判断是否需要认证
         * @param bean
         * @return true 不需要  false 需要
         */
        public static boolean isContains(NotAuthorizationBean bean)
        {
            List<String> paths = bean.getPaths();
            String name = bean.getName();
            for(String path : paths)
            {
                if(path.equalsIgnoreCase(name))
                {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 统一返回前端json数据
         * @param response
         * @param data
         */
        public static void writeResponse(HttpServletResponse response, Object data)
        {
            try {
                response.setContentType("application/json");
                OutputStream outputStream = response.getOutputStream();
                outputStream.write(JSON.toJSONString(data).getBytes("UTF-8"));
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public enum AuthorizationStatus {
        NOT_LOGIN(401,"没有登录"),
        NOT_AUTHORIZATION(403,"没有授权")
        ;
    
        private Integer code;
    
        private String msg;
    
        AuthorizationStatus(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    public class Result {
    
        private Integer c;
    
        private String d;
    
        public Result(AuthorizationStatus status)
        {
            this.c = status.getCode();
            this.d = status.getMsg();
        }
    
        public Result(Integer c, String d) {
            this.c = c;
            this.d = d;
        }
    
        public Integer getC() {
            return c;
        }
    
        public void setC(Integer c) {
            this.c = c;
        }
    
        public String getD() {
            return d;
        }
    
        public void setD(String d) {
            this.d = d;
        }
    }

    myFilter的实现类似,也是重写isAccessAllowed和onAccessDenied两个方法

    public class MyFilter extends PermissionsAuthorizationFilter {
        @Override
        public boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws IOException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
    
            String path = request.getServletPath();
            System.out.println("request path = " + path);
            Subject subject = getSubject(servletRequest,servletResponse);
            if(path.equals("/RPCAFA2A208FA648EA27C1EC30CADFC8B3D"))
            {
                return true;
            }
           /* String[] perms = (String[])((String[])o);
            boolean isPermitted = true;
            if(perms != null && perms.length > 0) {
                if(perms.length == 1) {
                    if(!subject.isPermitted(perms[0])) {
                        isPermitted = false;
                    }
                } else if(!subject.isPermittedAll(perms)) {
                    isPermitted = false;
                }
            }*/
    
            return super.isAccessAllowed(servletRequest,servletResponse,o);
        }
        /**
         * 会话超时或权限校验未通过的,统一返回401,由前端页面弹窗提示
         */
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
                throws IOException {
            System.out.println("no permission");
            Subject subject = getSubject(request,response);
            if(subject.getPrincipal() == null)
            {
                ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_LOGIN));
            }else{
                ShiroUtil.writeResponse((HttpServletResponse) response,new Result(AuthorizationStatus.NOT_AUTHORIZATION));
            }
            return false;
        }
    }

    五、MyShiroRealm实现

    realm是权限和登录管理的具体实现,需要继承AuthorizingRealm,实现doGetAuthorizationInfo权限认证和doGetAuthenticationInfo登录认证。

    @Service
    public class MyShiroRealm extends AuthorizingRealm {
        @Resource
        private UserInfoService userInfoService;
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("权限认证doGetAuthorizationInfo()");
            String username = (String) super.getAvailablePrincipal(principalCollection);
            System.out.println("username = " + username);
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //simpleAuthorizationInfo.addRole("admin");
            simpleAuthorizationInfo.addStringPermission("admin:test");
            return simpleAuthorizationInfo;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("登陆认证doGetAuthenticationInfo()");
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            System.out.println("token = " + token.getUsername());
            UserInfo userInfo = userInfoService.findByUsername(token.getUsername());
            if(userInfo != null)
            {
                return new SimpleAuthenticationInfo(userInfo.getUsername(),userInfo.getPassword(),getName());
            }
            return null;
        }
    }
  • 相关阅读:
    发现不错的cache系统Cache Manager Documentation
    List.Sort用法
    Database Initialization Strategies in Code-First:
    git rebase
    osharpV3数据库初始化
    IdentityDbContext
    AspNetUsers
    VS2015 推荐插件
    ELMAH日志组件数据库脚本
    C#如何把List of Object转换成List of T具体类型
  • 原文地址:https://www.cnblogs.com/xiaosiyuan/p/6655058.html
Copyright © 2011-2022 走看看