Spring аутентификация безопасности на основе параметра запроса

В приложении, над которым я работаю, уже есть Spring Security для обработки проверки подлинности на основе форм. Теперь необходимо войти в систему пользователя программно через внешнюю службу , если найден токен в одном из параметров запроса.

Другими словами, если существует определенный параметр запроса, например "токен", ему необходимо вызвать внешнюю службу с этим токеном, чтобы проверить, является ли он действительным токеном. Если это то, то пользователь войдет в систему.

Я не могу понять, как и где "запускать" или "подключаться к" Spring "Безопасность", чтобы проверить этот параметр и сделать проверку, а затем, при необходимости, аутентифицировать пользователя, поскольку нет формы входа в систему. Я думал, что в Spring безопасности должно быть что-то, что можно расширить или настроить для этого?

Я просмотрел документацию по безопасности Spring и задаюсь вопросом, правильно ли начать работу с AbstractPreAuthenticatedProcessingFilter?

Ответы

Ответ 1

У меня аналогичная настройка в моем приложении. Вот основные элементы, насколько я могу судить:

Вам нужно создать AuthenticationProvider так:

public class TokenAuthenticationProvider implements AuthenticationProvider {

    @Autowired private SomeService userSvc;

    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        if (auth.isAuthenticated())
            return auth;

        String token = auth.getCredentials().toString();
        User user = userSvc.validateApiAuthenticationToken(token);
        if (user != null) {
            auth = new PreAuthenticatedAuthenticationToken(user, token);
            auth.setAuthenticated(true);
            logger.debug("Token authentication. Token: " + token + "; user: " + user.getDisplayName());
        } else
            throw new BadCredentialsException("Invalid token " + token);
        return auth;
    }
}

Вам также необходимо создать Filter, чтобы включить настраиваемый параметр в токен аутентификации:

public class AuthenticationTokenFilter implements Filter {


    @Override
    public void init(FilterConfig fc) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
        SecurityContext context = SecurityContextHolder.getContext();
        if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
            // do nothing
        } else {
            Map<String,String[]> params = req.getParameterMap();
            if (!params.isEmpty() && params.containsKey("auth_token")) {
                String token = params.get("auth_token")[0];
                if (token != null) {
                    Authentication auth = new TokenAuthentication(token);
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            }
        }

        fc.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }

    class TokenAuthentication implements Authentication {
        private String token;
        private TokenAuthentication(String token) {
            this.token = token;
        }
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return new ArrayList<GrantedAuthority>(0);
        }
        @Override
        public Object getCredentials() {
            return token;
        }
        @Override
        public Object getDetails() {
            return null;
        }
        @Override
        public Object getPrincipal() {
            return null;
        }
        @Override
        public boolean isAuthenticated() {
            return false;
        }
        @Override
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        }
        @Override
        public String getName() {
            // your custom logic here
        }
    }

 }

Для этого вам необходимо создать beans:

<beans:bean id="authTokenFilter" class="com.example.security.AuthenticationTokenFilter" scope="singleton" />
<beans:bean id="tokenAuthProvider" class="com.example.security.TokenAuthenticationProvider" />

Наконец, вам нужно подключить эти beans к вашей конфигурации безопасности (соответственно отрегулируйте):

<sec:http >
   <!-- other configs here -->
   <sec:custom-filter ref="authTokenFilter" after="BASIC_AUTH_FILTER" /> <!-- or other appropriate filter -->
</sec:http>

<sec:authentication-manager>
    <!-- other configs here -->
    <sec:authentication-provider ref="tokenAuthProvider" />
</sec:authentication-manager>

Возможно, существует другой способ, но это определенно работает (используя Spring Security 3.1 на данный момент).

Ответ 2

Если вы используете Spring контроллер или службу MVC, где передается параметр запроса на печать, вы можете использовать аннотацию безопасности @PreAuthorize Spring.

Скажем, у вас есть служба Spring, которая может проверять пройденный токен и выполнять аутентификацию, если прошедший токен действителен:

@Service("authenticator")
class Authenticator {        
...
public boolean checkTokenAndAuthenticate(Object token) {
    ...
    //check token and if it is invalid return "false"
    ...
    //if token is valid then perform programmatically authentication and return "true"  
}
...             
}    

Затем с помощью Spring security @PreAuthorize аннотации вы можете сделать это следующим образом:

...
@PreAuthorize("@authenticator.checkTokenAndAuthenticate(#token)")
public Object methodToBeChecked(Object token) { ... }
...

Кроме того, вы должны включить Spring аннотации безопасности и добавить spring -security-аспекты в POM (или jar to classpath).