带有REST API的Spring Security

瓦西夫·科曼尼(Wasif Kirmani)

我试图创建一个主要使用Spring访问REST API的应用程序,并试图配置安全性。尝试使用此图片展示应用程序的实际结构:在此处输入图片说明

  1. 请求可以从任何平台发送到“ abc.com/rest_api/”
  2. 请求将发送到第3点或第5点。如果用户已经通过用户名和密码进行了身份验证,则将根据Token验证请求,否则将重定向到数据库。
  3. 如果用户名和密码必须由数据库进行身份验证,则将生成令牌并作为响应发送回去。
  4. 之后,仅基于令牌的身份验证将起作用。

我试图创建一个基本结构,但我认为必须犯一个小错误,因为它无法按预期运行。

@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private NSecurityContextHolder securityContextHolder;

    @Autowired
    private NHttpServletRequestBinder<Authentication> authenticationBinder;

    public static final String DEF_USERS_BY_USERNAME_QUERY
            = "SELECT user ";


public static final String GROUPS_BY_USERNAME_QUERY =
        "SELECT groups by user";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
        "SELECT  authorities";


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
                .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
                authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
                groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);


    }
      private DataSource getDataSourceFromJndi() {
        try {


             DataSource dataSource = (DataSource) new InitialContext().lookup("DS");
            return dataSource;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

      @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
                .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
                authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
                groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);

    }


      @Override
    protected void configure(HttpSecurity http) throws Exception {


                // The http.formLogin().defaultSuccessUrl("/path/") method is required when using stateless Spring Security
        // because the session cannot be used to redirect to the page that was requested while signed out. Unfortunately
        // using this configuration method will cause our custom success handler (below) to be overridden with the
        // default success handler. So to replicate the defaultSuccessUrl("/path/") configuration we will instead
        // correctly configure and delegate to the default success handler.
        final SimpleUrlAuthenticationSuccessHandler delegate = new SimpleUrlAuthenticationSuccessHandler();
        delegate.setDefaultTargetUrl("/api/");
          // Make Spring Security stateless. This means no session will be created by Spring Security, nor will it use any
        // previously existing session.
        http.sessionManagement().sessionCreationPolicy(STATELESS);
        // Disable the CSRF prevention because it requires the session, which of course is not available in a
        // stateless application. It also greatly complicates the requirements for the sign in POST request.
        http.csrf().disable();
        // Viewing any page requires authentication.
        http.authorizeRequests().anyRequest().authenticated();
        http
          .formLogin().loginPage("http://localhost/web/ui/#access/signin")
          .permitAll()
            // Override the sign in success handler with our stateless implementation. This will update the response
            // with any headers and cookies that are required for subsequent authenticated requests.
            .successHandler(new NStatelessAuthenticationSuccessHandler(authenticationBinder, delegate));
        http.logout().logoutUrl("http://localhost/web/ui/#access/signin").logoutSuccessUrl("http://localhost/web/ui/#access/signin");
        // Add our stateless authentication filter before the default sign in filter. The default sign in filter is
        // still used for the initial sign in, but if a user is authenticated we need to acknowledge this before it is
        // reached.
        http.addFilterBefore(
            new StatelessAuthenticationFilter(authenticationBinder, securityContextHolder),
            UsernamePasswordAuthenticationFilter.class
        );

} 

}

我有两种身份验证绑定程序,即TokenBased和UserNameBased。

基于令牌:

@Component
public class NXAuthTokenHttpServletRequestBinder implements NHttpServletRequestBinder<String> {

    private static final String X_AUTH_TOKEN = "X-AUTH-TOKEN";
    private final NTokenFactory tokenFactory;

    @Autowired
    public NXAuthTokenHttpServletRequestBinder(NTokenFactory tokenFactory) {
        this.tokenFactory = tokenFactory;
    }

    @Override
    public void add(HttpServletResponse response, String username) {

        final String token = tokenFactory.create(username);

        response.addHeader(X_AUTH_TOKEN, token);
        response.addCookie(new Cookie(X_AUTH_TOKEN, token));
    }

    @Override
    public String retrieve(HttpServletRequest request) {

        final String cookieToken = findToken(request);

        if (cookieToken != null) {
            return tokenFactory.parseUsername(cookieToken);
        }

        return null;
    }

    private static String findToken(HttpServletRequest request) {
        Enumeration<String> it = request.getHeaderNames();
        while(it.hasMoreElements()){
            System.out.println(it.nextElement());
        }
        final String headerToken = request.getHeader(X_AUTH_TOKEN);

        if (headerToken != null) {
            return headerToken;
        }

        final Cookie[] cookies = request.getCookies();

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (X_AUTH_TOKEN.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }
}

基于用户:

@Component
@Primary
public class NUserAuthenticationFactory implements NHttpServletRequestBinder<Authentication> {

    private final NHttpServletRequestBinder<String> httpServletRequestBinder;

    @Autowired

    public NUserAuthenticationFactory(NHttpServletRequestBinder<String> httpServletRequestBinder) {
        this.httpServletRequestBinder = httpServletRequestBinder;
    }

    @Override
    public void add(HttpServletResponse response, Authentication authentication) {
        httpServletRequestBinder.add(response, authentication.getName());
    }

    @Override
    public UserAuthentication retrieve(HttpServletRequest request) {

        final String username = httpServletRequestBinder.retrieve(request);

        if (username != null) {

            return new UserAuthentication(new CustomJDBCDaoImpl().loadUserByUsername(username));
        }

        return null;
    }
}

问题每当我加载我的应用程序时,它都会进入基于用户的身份验证,然后尝试从令牌中获取用户名,而不是从数据库中进行验证。但是,到那时,还没有令牌,因为这是我从UI发出的第一个发布请求。然后将我重定向回同一登录页面。

日志:

精细:/位于附加过滤器链中12的位置1;触发过滤器:'WebAsyncManagerIntegrationFilter'精细:/在附加过滤器链中12的位置2中;触发过滤器:'SecurityContextPersistenceFilter'精细:/在附加过滤器链中12的位置3中;触发Filter:'HeaderWriterFilter'罚款:
不注入HSTS标头,因为它与requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@a837508不匹配罚款:/位于附加过滤器链中12的位置4;触发过滤器:'LogoutFilter'罚款:检查请求的匹配:'/'; 针对' http:// localhost / web / ui /#access / signin'精细:/位于附加过滤器链中12的位置5处;触发过滤器:'StatelessAuthenticationFilter'精细:/在附加过滤器链中12的位置6中;触发筛选器:'UsernamePasswordAuthenticationFilter'罚款:请求'GET /'与'POST http:// localhost / web / ui /#access / signin不匹配精细:/在附加过滤器链中的12的位置7中;触发过滤器:'RequestCacheAwareFilter'精细:/在附加过滤器链中的12的位置8;触发过滤器:'SecurityContextHolderAwareRequestFilter'精细:/位于附加过滤器链中12的位置9;触发过滤器:'AnonymousAuthenticationFilter'罚款:填充的SecurityContextHolder,带有匿名令牌:'org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6:主体:onymousUser; 凭证:[受保护];已验证:true;详细信息:org.springframework.security.web.authentication.WebAuthenticationDetails@957e:RemoteIpAddress:127.0.0.1; SessionId:null;授予的权限:ROLE_ANONYMOUS'罚款:/位于附加过滤器链中第12个位置的10;触发筛选器:'SessionManagementFilter'罚款:请求的会话ID 3e2c15a2a427bf47e51496d2a186无效。精细:/位于附加过滤器链中12的位置11;触发过滤器:'ExceptionTranslationFilter'精细:/在附加过滤器链中12的位置12中;触发Filter:'FilterSecurityInterceptor'罚款:安全对象:FilterInvocation:URL:/; 属性:[已认证]精细:先前已认证:org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6:主体:anonymousUser;凭证:[受保护];已验证:true;详细信息:org.springframework.security.web.authentication.WebAuthenticationDetails@957e:RemoteIpAddress:127.0.0.1; SessionId:null;授予的权限:ROLE_ANONYMOUS罚款:选民:org.springframework.security.web.access.expression.WebExpressionVoter@2ac71565,返回:-1罚款:访问被拒绝(用户是匿名的);重定向到身份验证入口点org.springframework.security.access.AccessDeniedException:访问被拒绝

激战

逐步解决您的问题即可。

  1. 使用spring securityUsernamePasswordAuthenticationFilter通过用户名和密码对用户进行身份验证并生成唯一令牌。
  2. 编写另一个自定义安全筛选器和AuthenticationProvider实现,以验证用户的连续请求。
  3. UsernamePasswordAuthenticationFilter如下所示放置自定义安全过滤器

    http.addFilterBefore(CustomTokenBasedAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);

  4. 向您注册AuthenticationProvider实施AuthenticationManager

  5. 就是这样!!!

注意:保护其余API的更好方法是使用一些标准协议,例如oauth1a,oauth2.0等。Spring提供了oauth1a和oauth2.0协议的新颖实现。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

带有Spring Security的JSON REST API

来自分类Dev

Spring Security REST API

来自分类Dev

带有REST服务的Spring Integration Security示例

来自分类Dev

带有REST体系结构的Spring Security

来自分类Dev

带有REST服务的Spring Integration Security示例

来自分类Dev

带有Java配置的Rest API的Spring Security HTTP基本身份验证

来自分类Dev

Spring Security和REST API?

来自分类Dev

访问受Spring Security保护的REST API

来自分类Dev

使用Spring Security保护REST API

来自分类Dev

带有Spring Boot的Spring Security

来自分类Dev

带有spring-security和Basic Auth的REST-Service身份验证

来自分类Dev

带有主干URL的Spring Security

来自分类Dev

405在/ api / login OPTIONS请求中不允许使用带有grails-spring-security-rest插件的方法(战斗仍在继续...)

来自分类常见问题

Spring Security Basic Auth带有基于HTTP的摘要,用于无状态API

来自分类Dev

Spring Security-REST API-令牌与Cookie

来自分类Dev

带有Spring Security的Java Spring冲突依赖版本

来自分类Dev

带有REST API的CQRS

来自分类Dev

带有前端的REST API

来自分类Dev

RESTful API的Spring Security

来自分类Dev

Grails Spring Security Rest注销

来自分类Dev

Spring Security REST和Angular

来自分类Dev

Grails Spring Security REST + LDAP

来自分类Dev

Spring Security阻止Rest Controller

来自分类Dev

通过Spring Security保护REST

来自分类Dev

带有多个路径的Spring Security http antMatcher

来自分类Dev

带有Spring Security CAS的java.lang.NullPointerException

来自分类Dev

带有Spring Security CORS问题的AngularJS身份验证

来自分类Dev

带有allowAll()和过期的Auth令牌的URL的Spring Security

来自分类Dev

Spring Security-仅允许带有前缀的请求

Related 相关文章

热门标签

归档