C++验证redis返回的数据是不是为空

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++验证redis返回的数据是不是为空相关的知识,希望对你有一定的参考价值。

参考技术A 1.String——字符串String数据结构是简单的key-value类型,value不仅可以是String,也可以是数字(当数字类型用Long可以表示的时候encoding就是整型,其他都存储在sdshdr当做字符串)。使用Strings类型,可以完全实现目前Memcached的功能,并且效率更高。还可以享受Redis的定时持久化(可以选择RDB模式或者AOF模式),操作日志及Replication等功能。除了提供与Memcached一样的get、set、incr、decr等操作外,Redis还提供了下面一些操作:2.Hash——字典在Memcached中,我们经常将一些结构化的信息打包成hashmap,在客户端序列化后存储为一个字符串的值(一般是JSON格式),比如用户的昵称、年龄、性别、积分等。这时候在需要修改其中某一项时,通常需要将字符串(JSON)取出来,然后进行反序列化,修改某一项的值,再序列化成字符串(JSON)存储回去。简单修改一个属性就干这么多事情,消耗必定是很大的,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。3.List——列表List说白了就是链表(redis使用双端链表实现的List),相信学过数据结构知识的人都应该能理解其结构。使用List结构,我们可以轻松地实现最新消息排行等功能(比如新浪微博的TimeLine)。List的另一个应用就是消息队列,可以利用List的*PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段元素的API,你可以直接查询,删除List中某一段的元素。4.Set——集合Set就是一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的Set数据结构,可以存储一些集合性的数据。比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。因为Redis非常人性化的为集合提供了求交集、并集、差集等操作,那么就可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。1.共同好友、二度好友2.利用唯一性,可以统计访问网站的所有独立IP3.好友推荐的时候,根据tag求交集,大于某个threshold就可以推荐5.SortedSet——有序集合和Sets相比,SortedSets是将Set中的元素增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的SortedSets,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。另外还可以用SortedSets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

使用 Spring Session Redis 时身份验证主体为空

【中文标题】使用 Spring Session Redis 时身份验证主体为空【英文标题】:Authentication Principal is empty while using Spring Session Redis 【发布时间】:2016-07-30 06:08:56 【问题描述】:

我正在使用 Spring Boot v1.3.3 构建 rest API。 API 由 Spring Security 保护。我已经实现了自定义用户详细信息服务,以便在身份验证上下文中拥有自定义主体。

我需要与其他 Spring 应用程序共享 API 会话,因此我选择使用本教程在我的应用程序中使用 Redis 服务器实现 Spring Session docs.spring.io/spring-session/docs/current/reference/html5 /guides/security.html。不幸的是,它导致身份验证主体停止工作。当我试图通过注释@AuthenticationPrincipal CustomUserDetails userSecurityContextHolder.getContext().getAuthentication().getPrincipal() 获取当前主体时,它返回我的自定义用户详细信息,但Id = 0 和所有字段设置为null (screen from debugging)。我什至无法从SecurityContextHolder.getContext().getAuthentication().getName() 获取用户名。

在我评论 Redis 代码和 maven 依赖项后,它可以工作 (see debug screen)。如何让它与 Spring Session 和 Redis 服务器一起工作?

这是应用程序中的一些代码:

检查 Principal 的一些示例方法

@RequestMapping(value = "/status", method = RequestMethod.GET)
public StatusData status(@AuthenticationPrincipal CustomUserDetails user) 
    User user2 = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if (user != null) 
        String name = user.getUsername();
        return new StatusData(name);
     else return new StatusData(null);

应用程序和 Redis 配置:

@Configuration
@EnableRedisHttpSession
public class AppConfig 

    @Bean
    public JedisConnectionFactory connectionFactory() 
        return new JedisConnectionFactory();
    

    @Bean
    public CookieSerializer cookieSerializer() 
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("JSESSIONID");
        serializer.setCookiePath("/");
        serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
        return serializer;
    

    @Bean
    public ShaPasswordEncoder shaEncoder() 
        return new ShaPasswordEncoder(256);
    

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Bean(name = "messageSource")
    public ResourceBundleMessageSource messageSource() 
        ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
        resourceBundleMessageSource.setBasename("messages/messages");
        return resourceBundleMessageSource;
    

    @Bean
    public Validator basicValidator() 
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource());
        return validator;
    

    public AppConfig() 
        DateTimeZone.setDefault(DateTimeZone.UTC);
    

初始化器(用于 Redis 会话)

public class Initializer extends AbstractHttpSessionApplicationInitializer 


SecurityInitializer(用于 Redis 会话)

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer 

    public SecurityInitializer() 
        super(WebSecurityConfig.class, AppConfig.class);
    

WebSecurityConfig(Spring 安全配置)

@Configuration
@EnableWebSecurity
//@EnableWebMvcSecurity
@ComponentScan(basePackageClasses = UserRepository.class, CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService customUserDetailsService;

    @Autowired
    private HttpAuthenticationEntryPoint httpAuthenticationEntryPoint;

    @Autowired
    private AuthSuccessHandler authSuccessHandler;

    @Autowired
    private AuthFailureHandler authFailureHandler;

    @Autowired
    private HttpLogoutSuccessHandler logoutSuccessHandler;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * Persistent token repository stored in database. Used for remember me feature.
     */
    @Bean
    public PersistentTokenRepository tokenRepository() 
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
        db.setDataSource(dataSource);
        return db;
    

    /**
     * Enable always remember feature.
     */
    @Bean
    public AbstractRememberMeServices rememberMeServices() 
        CustomTokenPersistentRememberMeServices rememberMeServices = new CustomTokenPersistentRememberMeServices("xxx", customUserDetailsService, tokenRepository());
        rememberMeServices.setAlwaysRemember(true);
        rememberMeServices.setTokenValiditySeconds(1209600);
        return rememberMeServices;
    

    /**
     * Configure spring security to use in REST API.
     * Set handlers to immediately return HTTP status codes.
     * Enable remember me tokens.
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint(httpAuthenticationEntryPoint)
                .and()
                .authorizeRequests()
                .antMatchers("/cookie", "/register", "/redirect/**", "/track/**")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .successHandler(authSuccessHandler)
                .failureHandler(authFailureHandler)
                .and()
                .logout()
                .permitAll().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .rememberMe().rememberMeServices(rememberMeServices())
                .and()
                .headers()
                .addHeaderWriter(new HeaderWriter() 
                    /**
                     * Header to allow access from javascript AJAX in chrome extension.
                     */
                    @Override
                    public void writeHeaders(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
                        String corsUrl = "https://mail.google.com";
                        if (httpServletRequest.getHeader("Origin") != null && httpServletRequest.getHeader("Origin").equals(corsUrl)) 
                            httpServletResponse.setHeader("Access-Control-Allow-Origin", "https://mail.google.com");
                            httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
                            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
                            httpServletResponse.setHeader("Access-Control-Expose-Headers", "Location");
                        
                    
                );
    

    /**
     * Set custom user details service to allow for store custom user details and set password encoder to BCrypt.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        auth
                .userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
    

Maven 依赖项

<dependencies>
    <dependency>
        <groupId>$project.groupId</groupId>
        <artifactId>models</artifactId>
        <version>$project.version</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.2.3.Final</version>
    </dependency>
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </dependency>
    <dependency>
        <groupId>org.jadira.usertype</groupId>
        <artifactId>usertype.core</artifactId>
        <version>3.1.0.CR1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>2.2.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-joda</artifactId>
    </dependency>
    <dependency>
        <groupId>com.maxmind.geoip2</groupId>
        <artifactId>geoip2</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.ganyo</groupId>
        <artifactId>gcm-server</artifactId>
        <version>1.0.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session</artifactId>
        <version>1.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <version>4.0.4.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

【问题讨论】:

【参考方案1】:

我解决了这个问题。原来,Spring-Session 序列化了 Principal 对象。我的 UserDetails 自定义实现是 Hibernate Model User 类的子类。我通过在我的自定义 UserDetailsUser 模型和该模型中使用的所有类中实现 Serializable 接口来解决它。

【讨论】:

我还有一个自定义的UserDetails,我改成实现Serializable(公共类MyUserDetails扩展了org.springframework.security.core.userdetails.User实现了Serializable)。我使用带有 JDBC 后端的 Spring Session,但列 principal_name 为 NULL。奇怪。 我同意@yglodt,这可能不是一个好的答案。如果您查看org.springframework.security.core.userdetails.UserDetails 类,它已经在扩展java.io.Serializable 接口 @BigDong 没错!事实上,这不是问题!升级 spring-boot 后我遇到了这个问题...有人找到解决方案吗? 找到了!我分享给大家:问题是安全链中的过滤顺序。我通过为 redis 配置@Order(Ordered.HIGHEST_PRECEDENCE) 设置更高的优先级来解决它(我设置了最高优先级,但也许更低的优先级就足够了)。现在我的主体已正确填充。【参考方案2】:

为了使它在我的情况下工作,我还必须确保以正确的顺序设置 Servlet 过滤器。

对我来说是:

...
<filter-name>CharacterEncodingFilter</filter-name>
...
<filter-name>springSessionRepositoryFilter</filter-name>
...
<filter-name>springSecurityFilterChain</filter-name>
...
<filter-name>csrfFilter</filter-name>
...

之后,principal 不再为空。

【讨论】:

【参考方案3】:

正如@yglodt 所说,问题在于过滤器在弹簧安全过滤器链中的顺序。

在Java Config方式中,只需给Redis配置类设置更高的优先级

@Configuration
@EnableRedisHttpSession
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RedisConfig extends AbstractHttpSessionApplicationInitializer 

    @Bean
    public JedisConnectionFactory connectionFactory() 
        return new JedisConnectionFactory();
    


我设置了最高优先级,但也许更低的优先级就足够了。

现在应该正确填充主体。

【讨论】:

【参考方案4】:

HttpSecurity 链的顺序很重要:

不起作用,并且将主体名称保留为空:

    .authorizeRequests()
    .antMatchers("/api/register").permitAll()
    .anyRequest().authenticated()

工作正确:

        .authorizeRequests()
        .anyRequest().authenticated()
        .antMatchers("/api/register").permitAll()

【讨论】:

以上是关于C++验证redis返回的数据是不是为空的主要内容,如果未能解决你的问题,请参考以下文章

怎么用ajax和js检验用户名是不是合法和不能为空

SQL sever 查找的结果如何判断是不是为空

SQL sever 查找的结果如何判断是不是为空

Laravel 要求数据库中是不是为空

调用API接口返回成功,但设备却没有收到消息怎么办

如何验证jsp表单提交不为空