Статьи

Добавьте проверку подлинности RememberMe с помощью Spring Security

Я упоминал в своем посте « Добавление входа в социальную сеть в блог Jiwhiz», что функция RememberMe не работает с Spring Social Security. Ну, это потому, что приложение не аутентифицирует пользователя по имени пользователя и паролю, и полностью зависит от социальных сайтов (таких как Google, Facebook и Twitter), чтобы сделать работу. Конфигурация Spring Security по умолчанию не может справиться с этой ситуацией. Spring Security может быть самым сложным программным обеспечением среди всех проектов Spring Portfolio . Для корректной работы очень простого веб-приложения с безопасностью требуется около 10 фильтров. Чтобы упростить разработку приложений, Spring Security предоставляет конфигурацию пространства имен, начиная с версии 2.0, для автоматической настройки всех необходимых компонентов вместе, поэтому разработчикам не нужно разбираться в деталях. Он работает очень хорошо для большинства веб-приложений, если только ваше приложение не отличается от традиционного.

После того, как я изменил процесс входа в систему с аутентификации по имени пользователя на Spring Social Security без пароля, старая конфигурация для запомнить меня больше не работала. В справочном документе Spring Security имеется очень мало объяснений об аутентификации «Помни меня» , поэтому я купил книгу Spring Security 3.1, написанную Робом Уинчем, руководителем проекта Spring Security. В книге есть целая глава, рассказывающая об услугах Remember-Me, и она очень помогла мне понять, как работает функция Remember-Me в Spring Security. После прочтения книги я чувствую, что намного легче читать исходный код Spring Security, а чтение исходного кода всегда доставляет удовольствие.

Поскольку я не храню пароли для учетных записей пользователей, TokenBasedRememberMeServices по умолчанию не может работать с моим приложением, и я не хочу создавать свои собственные RememberMeServices — слишком много работы. К счастью, существует еще один подход к постоянным токенам, который заключается в хранении токенов в базе данных и сравнении токенов в файлах cookie. Все, что мне нужно, это настроить PersistentTokenBasedRememberMeServices в моем приложении с PersistentTokenRepository для хранения токенов. Spring Security предоставляет реализацию PersistentTokenRepository в JDBC, и я обнаружил, что написать собственную реализацию MongoDB после прочтения исходного кода тривиально.

Первым шагом является сохранение данных PersistentRememberMeToken в MongoDB. Мне нужно добавить класс сущности домена для него:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Document(collection = 'RememberMeToken')
public class RememberMeToken extends BaseEntity{
 
    private String username;
 
    @Indexed
    private String series;
 
    private String tokenValue;
 
    private Date date;
 
... // getter/setter omitted
 
    public RememberMeToken(){
 
    }
 
    public RememberMeToken(PersistentRememberMeToken token){
        this.series = token.getSeries();
        this.username = token.getUsername();
        this.tokenValue = token.getTokenValue();
        this.date = token.getDate();
    }
 
}

Далее, используйте Spring Data, чтобы добавить хранилище для сущности:

1
2
3
4
public interface RememberMeTokenRepository extends MongoRepository<RememberMeToken, String>{
    RememberMeToken findBySeries(String series);
    List<RememberMeToken> findByUsername(String username);
}

Тогда единственное относительно тяжелое кодирование — это реализовать PersistentTokenRepository для MongoDB:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MongoPersistentTokenRepositoryImpl implements PersistentTokenRepository {
 
    private final RememberMeTokenRepository rememberMeTokenRepository;
 
    public MongoPersistentTokenRepositoryImpl(RememberMeTokenRepository rememberMeTokenRepository){
        this.rememberMeTokenRepository = rememberMeTokenRepository;
    }
 
    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        RememberMeToken newToken = new RememberMeToken(token);
        this.rememberMeTokenRepository.save(newToken);
    }
 
    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        RememberMeToken token = this.rememberMeTokenRepository.findBySeries(series);
        if (token != null){
            token.setTokenValue(tokenValue);
            token.setDate(lastUsed);
            this.rememberMeTokenRepository.save(token);
        }
 
    }
 
    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        RememberMeToken token = this.rememberMeTokenRepository.findBySeries(seriesId);
        return new PersistentRememberMeToken(token.getUsername(), token.getSeries(), token.getTokenValue(), token.getDate());
    }
 
    @Override
    public void removeUserTokens(String username) {
        List<RememberMeToken> tokens = this.rememberMeTokenRepository.findByUsername(username);
        this.rememberMeTokenRepository.delete(tokens);
    }
}

Остальная часть работы — вся конфигурация. Мне нужно связать их вместе в классе конфигурации Java:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Configuration
public class SocialAndSecurityConfig {
    @Inject
    private Environment environment;
 
    @Inject
    private AccountService accountService;
 
    @Inject
    private AuthenticationManager authenticationManager;
 
    @Inject
    private RememberMeTokenRepository rememberMeTokenRepository;
 
...
 
    @Bean
    public RememberMeServices rememberMeServices(){
        PersistentTokenBasedRememberMeServices rememberMeServices = new PersistentTokenBasedRememberMeServices(
                        environment.getProperty('application.key'), accountService, persistentTokenRepository());
        rememberMeServices.setAlwaysRemember(true);
        return rememberMeServices;
    }
 
    @Bean
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider(){
        RememberMeAuthenticationProvider rememberMeAuthenticationProvider =
                        new RememberMeAuthenticationProvider(environment.getProperty('application.key'));
        return rememberMeAuthenticationProvider;
    }
 
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        return new MongoPersistentTokenRepositoryImpl(rememberMeTokenRepository);
    }
}

Последний шаг заключается в добавлении службы запомнить меня в конфигурационный файл безопасности xml, который является последним фрагментом конфигурации XML, и мы не можем сейчас его устранить. (Обновление: новый проект Spring Security Java Config заменит конфигурацию xml на конфигурацию Java в Spring Security.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version='1.0' encoding='UTF-8'?>
    xsi:schemaLocation='
 
    <http use-expressions='true' entry-point-ref='socialAuthenticationEntryPoint'>
        <custom-filter position='PRE_AUTH_FILTER' ref='socialAuthenticationFilter' />
        <logout logout-url='/signout' delete-cookies='JSESSIONID' />
        <remember-me services-ref='rememberMeServices' />
        <!-- Configure these elements to secure URIs in your application -->
        <intercept-url pattern='/favicon.ico' access='permitAll' />
        <intercept-url pattern='/robots.txt' access='permitAll' />
        <intercept-url pattern='/resources/**' access='permitAll' />
        <intercept-url pattern='/signin' access='permitAll'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/signin/*' access='permitAll'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/presentation/**' access='hasRole('ROLE_USER')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/myAccount/**' access='hasRole('ROLE_USER')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/myPost/**' access='hasRole('ROLE_AUTHOR')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/admin/**' access='hasRole('ROLE_ADMIN')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/**' access='permitAll' />
 
    </http>
 
    <authentication-manager alias='authenticationManager'>
        <authentication-provider ref='socialAuthenticationProvider' />
        <authentication-provider ref='rememberMeAuthenticationProvider' />
    </authentication-manager>
 
</beans:beans>

Это все, что нужно для добавления аутентификации Remember-Me в мое приложение для блога. Теперь вы можете войти через Google / Facebook / Twitter на мой сайт, и сайт будет постоянно помнить вас в течение следующих двух недель.

Ссылка: добавьте аутентификацию RememberMe с помощью Spring Security от нашего партнера по JCG Юаня Цзи в блоге Jiwhiz .