【问题标题】:Spring-Security : SecurityContextHolder not populated with anonymous token, as it already containedSpring-Security:SecurityContextHolder 没有填充匿名令牌,因为它已经包含
【发布时间】:2015-07-21 15:53:19
【问题描述】:

我正在开发一个使用 Spring-Security 进行身份验证和相关安全目的的 Spring-MVC 应用程序。目前我在查询 sessionRegistry 以获取当前在线用户的列表时遇到问题,因为 getAllPrincipals() 方法始终包含 NULL。我尝试了文档中提到的解决方案以及其他 SO 线程中提到的许多建议。我真的很感激一些帮助,因为我正在尝试各种组合,这些组合将有助于获取所有在线用户的列表。

这是我的 security-application-context.xml:

     <import resource="servlet-context.xml" />

    <!-- Global Security settings -->
    <security:global-method-security pre-post-annotations="enabled" />
    <security:http pattern="/resources/**" security="none"/>

    <security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
        <security:form-login login-page="/login" login-processing-url="/j_spring_security_check" default-target-url="/canvas/list" always-use-default-target="false" authentication-failure-url="/denied.jsp" />
        <security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService" token-validity-seconds="1209600" data-source-ref="dataSource"/>
        <security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
    <security:port-mappings>
        <security:port-mapping http="80" https="443"/>
    </security:port-mappings>
    <security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
    </security:http>
        <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
        <beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
            <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
            <beans:property name="maximumSessions" value="-1" />
        </beans:bean>
  <beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
        <beans:property name="key" value="_spring_security_remember_me" />
        <property name="alwaysRemember" value="true"/>
        <beans:property name="tokenRepository" ref="jdbcTokenRepository"/>
        <beans:property name="userDetailsService" ref="LoginServiceImpl"/>
    </beans:bean>
 <beans:bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="sessionAuthenticationStrategy" ref="sas"/>
        <property name="rememberMeServices" ref="rememberMeAuthenticationProvider"/>
        <property name="allowSessionCreation" value="true"/>
        <property name="authenticationManager" ref="authenticationManager"/>
    </beans:bean>
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider user-service-ref="LoginServiceImpl">
           <security:password-encoder  ref="encoder"/>
        </security:authentication-provider>
    </security:authentication-manager>
    <beans:bean id="encoder"             class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
        <beans:constructor-arg name="strength" value="11" />
    </beans:bean>
    <beans:bean id="daoAuthenticationProvider"
                class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
                <beans:property name="userDetailsService" ref="LoginServiceImpl"/>
               <beans:property name="passwordEncoder" ref="encoder"/>
    </beans:bean>

这是我试图取回 OnlineUsers 列表的地方:

@Service
@Transactional
public class OnlineUsersServiceImpl implements OnlineUsersService {

  @Autowired
    @Qualifier("sessionRegistry")
    private SessionRegistry sessionRegistry;

 @Override
    public boolean checkIfUserhasSession(int id) {

        Person person = this.personService.getPersonById(id);
        String email = person.getUsername();
        List<Object> principals = sessionRegistry.getAllPrincipals();
        for (Object principal : principals) {
            // It never reaches here
            System.out.println("We reached here");
            if (principal instanceof User) {
                  String username = ((User) principal).getUsername();
                System.out.println("Username is "+username);
                    if(email.equals(username)){
                        return true;
                    }

            }
        }
        return false;
    }

任何帮助都会很好。因为我自 3-4 天以来一直在做这件绝望的事情。

【问题讨论】:

    标签: java spring spring-mvc spring-security session-state


    【解决方案1】:

    在注入会话注册表之前,您需要在 security-application-context.xml 中定义会话管理部分

    在并发控制部分你应该为会话注册对象设置别名

    <security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
            <security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true"> 
                <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
            </security:session-management>
    
        ...
        </security:http>
    

    基于 java 的配置替代 xml 配置

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(final HttpSecurity http) throws Exception {
            // ...
            http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
        }
    
        @Bean
        public SessionRegistry sessionRegistry() {
            return new SessionRegistryImpl();
        }
    
        @Bean
        public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
            return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
        }
    }
    

    【讨论】:

    • 如果我默认使用 5 个会话可以吗,因为我们希望在多个设备上拥有多个会话。另外,我没有你上面提到的文件,我想它不会影响任何其他功能。
    • 顺便说一句,你从 Spring-Boot 给了我 ServletListenerRegistrationBean,我没有使用 spring-boot。我正在使用 Spring-MVC。我收到一个错误 ServletListenerRegistrationBean,无法解析符号。因此,受保护的 void 配置方法不会覆盖其超类中的方法。
    • WebMVCConfigurerAdapter.java 的 pastebin 没有名为 configre 的方法。粘贴链接:pastebin.com/2PdU5nFA
    • Java 部分和 xml 可以相互替代我已经编辑了文件名。
    • 试过你的新代码,对不起,它没有改变任何东西。你想让我放一些其他文件以便你了解发生了什么吗?
    【解决方案2】:

    您需要在 web.xml 中进行额外配置 - 登录/注销事件的侦听器

    <listener>
       <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
       </listener-class>
    </listener>
    

    【讨论】:

      猜你喜欢
      • 2016-08-08
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2013-07-06
      • 2020-01-28
      • 2020-11-02
      • 1970-01-01
      • 2020-12-06
      相关资源
      最近更新 更多