Spring安全性 - 一个REST API - 两种不同的身份验证

时间:2017-07-26 14:47:58

标签: spring authentication spring-security token jsessionid

我目前正在为我的大学开发网络应用程序。大学为我提供授权服务器。

我正在使用rest api为我的角度前端提供数据。在后端我使用spring boot + spring security

我正在使用此url:/ classification-login进行登录,因此当用户访问此URL时,他将被重定向到授权服务器,登录,...,您知道它是怎么回事。如果我理解正确,结果是JSESSIONID保存在浏览器cookie中,应用程序通过JSESSIONID识别用户并从其会话中获取用户名。

我需要的是为其他网络应用程序使用相同的REST API,因此用户在完全不同的应用程序中登录,另一个应用程序获取其访问令牌,然后使用此令牌访问我的API。问题是我的应用程序只能通过JSESSIONID识别用户。所以我的问题是,如何设置spring security以首先通过jsessionid检查用户,如果访问令牌不存在则。

感谢任何人的回复。

Java代码:

@SpringBootApplication
@EnableOAuth2Sso
@ComponentScan
@ImportResource({"classpath:classification-connector.xml", "classpath:classification-security.xml"})
public class Application extends WebSecurityConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
    http.logout().and().antMatcher("/**").authorizeRequests()
            .antMatchers("/classification-login").authenticated()
            .anyRequest().permitAll();
}

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}
}
yaml中的

配置:

debug: true
security:
  user:
    password: none
  oauth2:
    client:
      accessTokenUri: https://xxx/oauth/token
      userAuthorizationUri: https://xxx/oauth/authorize
      clientId: supersecret
      clientSecret: supersecret
      scope: read

    resource:
      tokenInfoUri: https://xxx/oauth/check_token

1 个答案:

答案 0 :(得分:1)

我在本文的帮助下找到了答案:http://automateddeveloper.blogspot.cz/2014/03/securing-your-mobile-api-spring-security.html

也许有更好的方法,但这是我的解决方案:

我使用了两个配置文件,首先是客户端应用程序和原始Web应用程序接收的资源,第二个是我的登录页面

import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;


@Configuration
@EnableWebSecurity
@Order(1)
public class ConfigApi extends WebSecurityConfigurerAdapter {

        @Override protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/api/**")
                    .csrf()
                    .disable()
                    .authorizeRequests().anyRequest().authenticated().and()
                    .addFilterBefore(new CustomFilter(), BasicAuthenticationFilter.class );
        }
}

首先配置在以/ api / **

开头的网址上的每个请求之前添加过滤器
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@EnableOAuth2Sso

@Order(2)
public class ConfigLogin extends WebSecurityConfigurerAdapter {


        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf()
                    .disable()
                    .authorizeRequests()
                    .antMatchers("/classification-login").authenticated();
        }
}

第二个配置说url / classification-login上的请求需要进行身份验证,但不添加任何过滤器。这意味着用户将被重定向到授权服务器,在那里他登录并且spring安全性将在他的会话中保存身份验证(使用JSESSIONID)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

public class CustomFilter extends GenericFilterBean {


    @Value("${security.oauth2.client.clientId}") 
    private String clientId;
    @Value("${security.oauth2.resource.tokenInfoUri}")
    private String checkToken;
    @Value("${security.oauth2.client.scope}")
    private String scope;

        @Override
        public void doFilter(
                ServletRequest request,
                ServletResponse response,
                FilterChain chain) throws IOException, ServletException {

                    try {
                        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                        final String authorization = httpServletRequest.getHeader("Authorization");
                        final String token = authorization.replace("Bearer ", "");

                        //Here I verify the user by token sent in headers (using tokenInfoUri of my authorization server)
                        final RestTemplate restTemplate = new RestTemplate();
                        final TokenInfo tokenInfo = restTemplate.getForObject(checkToken + "?token=" + token, TokenInfo.class);
                        final String userName = tokenInfo.getUserName();

                        final Set<String> scopes = new HashSet<>();
                        scopes.add(scope);
                        final OAuth2Request oAuth2Request = new OAuth2Request(Collections.<String, String>emptyMap(), clientId, null, true, scopes, null, null, null, null);
                        final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
                        final UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, null, authorities);
                        final OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, usernamePasswordAuthenticationToken);
                        oAuth2Authentication.setAuthenticated(true);
                        final SecurityContextImpl securityContext = (SecurityContextImpl) SecurityContextHolder.getContext();
                        securityContext.setAuthentication(oAuth2Authentication);
                    } catch (Exception ignore) {
                        System.out.println(ignore);
                    }
            chain.doFilter(request, response);
        }
}

这是我的过滤器,基本上我只是检查请求中的标头,读取令牌并自己通过令牌验证用户。 (如果没有令牌,它会捕获异常并继续[将来可能会做得更好,现在没时间了])

结果:

如果用户使用我的webapp,他使用/ classification-login登录,然后允许他使用api,因为spring security会在会话中保存他的身份验证。

如果有人想要使用他的应用程序中的api,他需要在他的应用程序中使用相同的授权服务器,获取他的令牌并在标题中传递请求。

如果有人知道更好的解决方案随时发表评论,我花了太多时间在这上面,所以我不打算进一步调查。