Shiro combat: interface permission control, shiro solves data parallel unauthorized security vulnerabilities

background

Yesterday, the network security brigade scanned the security leak, and mentioned that there was a fatal security loophole: the data interface parallel exceeded the authority, and the rectification was required within half a month. Data parallel ultra vires is in the vernacular: I first log in to the background system with a super administrator, and then I remember the URL corresponding to menu A. I am using a normal administrator to log in, but I do not have the authority of Menu A, but I can access it directly through the URL of Menu A.
The most fundamental problem with the vulnerability of data parallelism is that the system does not have access control at the interface level. Many systems act as interceptors for all interfaces, only verifying whether they are logged in, but not associating users, role permissions, and interfaces.
The following picture is a screenshot of the security vulnerability report of the Network Security Team:
Screenshot of the Security Vulnerability Report of the Network Security Team

The following is a solution to the java web framework.

solution

JAVA background changes:

  1. pom.xml introduces the jar package corresponding to shiro:
		<!-- 对shiro的支持 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro对redis的支持 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>2.4.2.1-RELEASE</version>
        </dependency>
  1. Add the following annotations to interfaces that require permission control:
@RequiresPermissions("student:getStudent")
    @RequestMapping(value = "getStudent",method= RequestMethod.POST)
    public ModelAndView getStudent()
    {
    
    
    	return new ModelAndView("/student");
    }
  1. Application.yml configure shiro and redis information:
spring:
  redis:
    shiro :
      host: 127.0.0.1
      port: 6379
      timeout : 0
      password:
  1. Unified exception handling class: MyExceptionHandler.java
@Slf4j
public class MyExceptionHandler implements HandlerExceptionResolver {
    
    

    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
    
    
    	ex.printStackTrace();
        ModelAndView mv = new ModelAndView();
        FastJsonJsonView view = new FastJsonJsonView();
        Map<String, Object> attributes = new HashMap<String, Object>();
        if (ex instanceof UnauthenticatedException) {
    
    
            attributes.put("sucess", "false");
            attributes.put("info", "token错误");
        } else if (ex instanceof UnauthorizedException) {
    
    
            attributes.put("sucess", "false");
            attributes.put("info", "用户无权限");
        } else {
    
    
            attributes.put("sucess", "false");
            attributes.put("info", ex.getMessage());
            log.error("发生未处理的异常={}",ex.getMessage(),ex);
        }

        view.setAttributesMap(attributes);
        mv.setView(view);
        return mv;
    }
}

  1. New permission control: MyShiroRealm.java
public class MyShiroRealm extends AuthorizingRealm {
    
    
    @Resource
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;

    /**
     * 获取授权信息(把数据库中shiroID加入到shiro中管理,然后如果ctronler层方法加上@RequiresPermissions注解才会调用这个方法和数组进行比对)
	 * 只有当需要检测用户权限的时候才会调用此方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
	{
    
    
		//获取username
		SysUser sysUser=(SysUser) SecurityUtils.getSubject().getSession().getAttribute("sysUserLogin");
		List<SysPrivilege> sysPrivilegeList=sysPrivilegeList=sysRoleService.getSysPrivilegeByRoid(sysUser.getRoleId());
		// 用户权限列表
		Set<String> permsSet = new HashSet<>();
		for (SysPrivilege perms : sysPrivilegeList)
		{
    
    
			if (StringUtils.isBlank(perms.getShiroID()))
			{
    
    
				continue;
			}
			permsSet.addAll(Arrays.asList(perms.getShiroID().trim().split(",")));
		}
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.addStringPermissions(permsSet);
		return info;
	}

    /**
     * 获取身份验证信息
     * 主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
    
    
//        System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
        //获取用户的输入的账号.
        String username = (String) token.getPrincipal();
//        System.out.println(token.getCredentials());
        //通过username从数据库中查找 User对象,如果找到,没找到.
        //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        SysUser userInfo = sysUserService.getByUserCode(username);
//        System.out.println("----->>userInfo="+userInfo);
        if (userInfo == null) {
    
    
            return null;
        }
        if (Integer.parseInt(userInfo.getStates()) == 1) {
    
     //账户冻结
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                token.getCredentials(), //用户名
                userInfo.getPassword(),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }

}
  1. New shiro management class: MySessionManager.java
public class MySessionManager extends DefaultWebSessionManager {
    
    

    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
    
    
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
    
    
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //适合于安卓,ios的或者前后端分离情况,直接在head头中传入sessionId的值(sessionId的键名为Authorization)
        if (!StringUtils.isEmpty(id)) {
    
    
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
    
    
            //适用于浏览器访问,否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }

}
  1. New shiro configuration rule class: ShiroConfig.java
@Configuration
public class ShiroConfig {
    
    

    @Value("${spring.redis.shiro.host}")
    private String host;
    @Value("${spring.redis.shiro.port}")
    private int port;
    @Value("${spring.redis.shiro.timeout}")
    private int timeout;
    @Value("${spring.redis.shiro.password}")
    private String password;

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    
    
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //注意过滤器配置顺序 不能颠倒
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl
        filterChainDefinitionMap.put("/loginout", "logout");
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/jqueryEasyui/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/theme/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/ajaxLogin", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/demoOrder/**r", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        shiroFilterFactoryBean.setLoginUrl("/index");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权界面;
//        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * )
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
    
    
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        return hashedCredentialsMatcher;
    }

    @Bean
    public MyShiroRealm myShiroRealm() {
    
    
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }


    @Bean
    public SecurityManager securityManager() {
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    //自定义sessionManager
    @Bean
    public SessionManager sessionManager() {
    
    
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(redisSessionDAO());
        return mySessionManager;
    }

    /**
     * 配置shiro redisManager
     * <p>
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
    
    
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setExpire(1800);// 配置缓存过期时间(登录是失效时间,0代表永不失效)
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * <p>
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
    
    
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * <p>
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
    
    
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

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

    /**
     * 注册全局异常处理
     * @return
     */
    @Bean(name = "exceptionHandler")
    public HandlerExceptionResolver handlerExceptionResolver() {
    
    
        return new MyExceptionHandler();
    }
}

Front end changes:

  1. The front end needs to make a unified bottom-level judgment based on its own framework and then jump to the unified permission exception page.
  2. The background interface configuration also needs to support the management of shiroid. The following effects:
    Insert picture description here
    Insert picture description here

Source address:

  1. Get the complete source code address: https://download.csdn.net/download/penggerhe/11670196
  2. Follow the official account and receive it for free:
    Insert picture description here

Guess you like

Origin blog.csdn.net/penggerhe/article/details/108225886