2015-10-24 119 views
4

我正在尝试创建一个应用程序,它将主要使用Spring访问REST API,并且正在尝试配置安全性方面。设法提出使用该画面中的应用程序的实际结构: enter image description hereSpring安全与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 
     ); 

} 

} 

而且我有两种类型的authenticationBinder,即TokenBased和UserNameBased。

TokenBased:

@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'罚款:/在位置2的 12在额外的过滤链中;点火过滤器: 'SecurityContextPersistenceFilter'罚款:/位于12的第3位 附加过滤器链;发射过滤器:'HeaderWriterFilter'罚款:
不注入HSTS头,因为它与requestMatcher org.springframework.security.web.header.writers不匹配。HstsHeaderWriter $ SecureRequestMatcher @ a837508 Fine:/位于12的第4位,位于额外的过滤器链中;射击 过滤器:'注销过滤器'罚款:检查请求匹配:'/'; 针对'http://localhost/web/ui/#access/signin'罚款:/位置 5 of 12 in additional filter chain;燃烧过滤器: 'StatelessAuthenticationFilter'罚款:/位于12的第6位 额外的过滤器链;发射过滤器: 'UsernamePasswordAuthenticationFilter'罚款:请求'GET /'不与 匹配'POST http://localhost/web/ui/#access/signin罚款:/在 位置7的12在另外的过滤器链中;点火过滤器: 'RequestCacheAwareFilter'罚款:/位于第12位的另外 过滤器链;发射滤波器:'SecurityContextHolderAwareRequestFilter' 精细:/位于附加滤波器链12的位置9;射击 过滤器:'AnonymousAuthenticationFilter'罚款:已填充 SecurityContextHolder with anonymous token: 'org.sprin[email protected]9055e4a6: Principal:anonymousUser;证书:[PROTECTED];已认证: true;详细信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予的权限: ROLE_ANONYMOUS'好:/在第12位的第10位附加过滤器 链;射击过滤器:'SessionManagementFilter'罚款:请求 会话ID 3e2c15a2a427bf47e51496d2a186无效。精细:/在 位置11的12在另外的过滤器链中;燃烧过滤器: 'ExceptionTranslationFilter'罚款:/位于12的12位 附加过滤链; “FilterSecurityInterceptor” Fine:Secure object:FilterInvocation:URL:/;属性: [已验证]正常:以前已通过身份验证: org.sprin[email protected]9055e4a6: 负责人:anonymousUser;证书:[PROTECTED];已认证: true;详细信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予权限: ROLE_ANONYMOUS罚款:选民: org.sp[email protected]2ac71565, 返回:-1罚款:访问被拒绝(用户是匿名的);重定向 到身份验证入口点 org.springframework.security.access.AccessDeniedException:访问被拒绝

回答

2

步骤明智的您的问题能够得到解决..

  1. 使用Spring Security UsernamePasswordAuthenticationFilter到通过用户名和密码对用户进行身份验证并生成唯一的令牌。
  2. 编写另一个自定义安全过滤器和AuthenticationProvider实现来验证用户的连续请求。下面

    http.addFilterBefore(CustomTokenBasedAuthenticationFilter,UsernamePasswordAuthenticationFilter给出befor UsernamePasswordAuthenticationFilter

  3. 将自定义的安全过滤器。类);

  4. AuthenticationManager

  5. 并且那它注册AuthenticationProvider实现!

注: -一个更好的办法,以确保您的REST API是使用一些标准协议,如oauth1a,OAuth2.0的等Spring提供了一个新颖的实施oauth1a和OAuth2.0的协议。