【问题标题】:Spring security - One REST API - two different authenticationsSpring security - 一个 REST API - 两种不同的身份验证
【发布时间】:2018-01-01 23:30:58
【问题描述】:

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

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

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

我需要为其他网络应用程序使用相同的 REST API,因此完全不同的应用程序中的用户登录,其他应用程序获取他的访问令牌,然后使用此令牌访问我的 API。问题是我的应用程序只能通过 JSESSIONID 识别用户。所以我的问题是,我如何设置 spring 安全性以首先通过 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

【问题讨论】:

    标签: spring authentication spring-security token jsessionid


    【解决方案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/** 开头的 url 请求之前添加过滤器

    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 security 将在他的会话中保存身份验证(使用 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,他需要在他的应用程序中使用相同的授权服务器,获取他的令牌并在请求头中传递它。

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

    【讨论】:

      猜你喜欢
      • 2013-12-05
      • 2019-02-07
      • 2014-03-18
      • 2016-03-20
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      相关资源
      最近更新 更多