Shiro Combat: contrôle des autorisations d'interface, Shiro résout les vulnérabilités de sécurité non autorisées en parallèle des données

Contexte

Hier, la brigade de sécurité du réseau a scanné la fuite de sécurité et a mentionné qu'il y avait une faille de sécurité fatale: l'interface de données parallèle dépassait l'autorité et la rectification était nécessaire dans un délai d'un demi-mois. Les données parallèles ultra vires sont dans la langue vernaculaire: je me connecte d'abord au système d'arrière-plan avec un super administrateur, puis je me souviens de l'URL correspondant au menu A. J'utilise un administrateur normal pour me connecter, mais je n'ai pas l'autorité du Menu A, mais je peux y accéder directement via l'URL du Menu A.
Le problème le plus fondamental de la vulnérabilité du parallélisme des données est que le système n'a pas de contrôle d'accès au niveau de l'interface. De nombreux systèmes agissent comme des intercepteurs pour toutes les interfaces, vérifiant uniquement s'ils sont connectés, mais n'associant pas les utilisateurs, les autorisations de rôle et les interfaces.
L'image suivante est une capture d'écran du rapport de vulnérabilité de sécurité de l'équipe de sécurité réseau:
Capture d'écran du rapport de vulnérabilité de sécurité de l'équipe de sécurité réseau

Ce qui suit est une solution au framework web java.

Solution

Changements d'arrière-plan JAVA:

  1. pom.xml introduit le package jar correspondant à 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. Ajoutez les annotations suivantes aux interfaces qui nécessitent un contrôle des autorisations:
@RequiresPermissions("student:getStudent")
    @RequestMapping(value = "getStudent",method= RequestMethod.POST)
    public ModelAndView getStudent()
    {
    
    
    	return new ModelAndView("/student");
    }
  1. Application.yml configure les informations shiro et redis:
spring:
  redis:
    shiro :
      host: 127.0.0.1
      port: 6379
      timeout : 0
      password:
  1. Classe de gestion des exceptions unifiée: 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. Nouveau contrôle des autorisations: 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. Nouvelle classe de gestion shiro: 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. Nouvelle classe de règles de configuration shiro: 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();
    }
}

Modifications du frontal:

  1. Le serveur frontal doit effectuer un jugement de niveau inférieur unifié basé sur son propre cadre, puis accéder à la page d'exception d'autorisation unifiée.
  2. La configuration de l'interface d'arrière-plan doit également prendre en charge la gestion du shiroid. Les effets suivants:
    Insérez la description de l'image ici
    Insérez la description de l'image ici

Adresse source:

  1. Obtenez l'adresse complète du code source: https://download.csdn.net/download/penggerhe/11670196
  2. Suivez le compte officiel et recevez-le gratuitement:
    Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/penggerhe/article/details/108225886
conseillé
Classement