2013-06-15 51 views
5

Tôi có một máy chủ có API. Máy chủ được bảo vệ bởi Spring Security.Xác thực bảo mật mùa xuân qua mã thông báo

Tôi muốn có quyền truy cập vào API từ một ứng dụng bên ngoài sử dụng thẻ trong yêu cầu thông số

Trước hết người dùng sẽ được chuyển tới một dịch vụ mang đến cho anh ta một token và sau đó truy cập vào API với dấu hiệu này.

Nhưng tôi muốn giữ quyền truy cập trước đó vào API thông qua các giải pháp bảo mật mùa xuân tiêu chuẩn.

Vì vậy, bạn có thể vui lòng giúp tôi, làm cách nào tôi có thể thực hiện việc này?

+0

Kiểm tra hỗ trợ OAuth. –

Trả lời

7

Bạn cần phải thực hiện AuthenticationFilter tùy chỉnh như

public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter { 

    private static final String SECURITY_TOKEN_KEY = "token"; 
    private static final String SECURITY_TOKEN_HEADER = "X-Token"; 
    private String token = null; 

    protected CustomAuthenticationFilter() { 
    super("/"); 
    } 

    @Override 
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 
    HttpServletRequest request = (HttpServletRequest) req; 
    HttpServletResponse response = (HttpServletResponse) res; 

    this.token = request.getParameter(SECURITY_TOKEN_KEY); 
    // or this.token = request.getHeader(SECURITY_TOKEN_HEADER); 

    if (request.getAttribute(FILTER_APPLIED) != null) { 
     chain.doFilter(request, response); 
     return; 
    } 

    request.setAttribute(FILTER_APPLIED, Boolean.TRUE); 

    if(request.getParameter(actionParameter) !=null && 
     request.getParameter(actionParameter).equals("logout")) { 
     SecurityContextHolder.clearContext(); 
     return; 
    } 

    if (!requiresAuthentication(request, response)) { 
     chain.doFilter(request, response); 
     return; 
    } 

    Authentication authResult; 
    try { 
     authResult = attemptAuthentication(request, response); 
     if (authResult == null) { 
     return; 
     } 
    } catch (AuthenticationException failed) { 
     unsuccessfulAuthentication(request, response, failed); 
     return; 
    } 

    try { 
     successfulAuthentication(request, response, chain, authResult); 
    } catch (NestedServletException e) { 
     if(e.getCause() instanceof AccessDeniedException) { 
     unsuccessfulAuthentication(request, response, new LockedException("Forbidden")); 
     } 
    } 
    } 

    @Override 
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { 

    AbstractAuthenticationToken userAuthenticationToken = authUserByToken(this.token); 
    if(userAuthenticationToken == null) 
     throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token")); 

    return userAuthenticationToken; 
    } 

    private AbstractAuthenticationToken authUserByToken(String tokenRaw) { 
    AbstractAuthenticationToken authToken = null; 
    try { 
     // check your input token, identify the user 
     // if success create AbstractAuthenticationToken for user to return 
     // eg: 
     authToken = new UsernamePasswordAuthenticationToken(username, userHash, userAuthorities); 

    } catch (Exception e) { 
     logger.error("Error during authUserByToken", e); 
    } 
    return authToken; 
    } 

    @Override 
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
              Authentication authResult) throws IOException, ServletException { 
    SecurityContextHolder.getContext().setAuthentication(authResult); 

    getSuccessHandler().onAuthenticationSuccess(request, response, authResult); 
    } 

} 

và tùy chỉnh SuccessHandler này như thế này

public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { 

    @Override 
    protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) { 
    return request.getServletPath(); 
    } 

    @Override 
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { 
    request.getRequestDispatcher(request.getServletPath()).forward(request, response); 
    } 
} 

và dây nó trong cấu hình mùa xuân

<?xml version="1.0" encoding="UTF-8"?> 
<b:beans 
    xmlns="http://www.springframework.org/schema/security" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:b="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:sec="http://www.springframework.org/schema/security" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd 
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> 

    <context:annotation-config/> 
    <context:component-scan base-package="com.your.path" /> 

    <aop:aspectj-autoproxy/> 

    <global-method-security pre-post-annotations="enabled" secured-annotations="enabled" proxy-target-class="true" 
          access-decision-manager-ref="accessDecisionManager"/> 

    <http entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" 
     auto-config="true" access-decision-manager-ref="accessDecisionManager"> 
    <custom-filter ref="restFilter" position="PRE_AUTH_FILTER"/> 
    <logout/> 
    </http> 

    <b:bean id="restAuthenticationEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/> 

    <b:bean id="restFilter" class="com.your.path.CustomAuthenticationFilter"> 
    <b:property name="authenticationSuccessHandler" ref="mySuccessHandler"/> 
    </b:bean> 

    <b:bean id="mySuccessHandler" class="com.your.path.CustomAuthenticationSuccessHandler"/> 

    <b:bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"> 
    <b:property name="allowIfAllAbstainDecisions" value="true"/> 
    <b:property name="decisionVoters"> 
     <b:list> 
     <b:bean class="org.springframework.security.access.vote.RoleVoter"> 
      <b:property name="rolePrefix" value=""/> 
     </b:bean> 
     <b:bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> 
     </b:list> 
    </b:property> 
    </b:bean> 

</b:beans> 

này sẽ giúp.

+0

Không có bộ lọc xác thực dựa trên mã thông báo trên bảo mật mùa xuân? – Rafael

4

Bạn có thể làm điều đó bằng đậu sau đây trong an ninh mùa xuân TimedKeyBasedPersistenceTokenService

<bean name="tokenService" class="com.digipos.security.core.token.TimedKeyBasedPersistenceTokenService"> 
    <property name="tokenLifeInMinutes" value="15000"/> 
    <property name="serverSecret" value="1234567"/> 
    <property name="serverInteger" value="15062013"/> 
    <property name="pseudoRandomNumberBits" value="7"/> 
    <property name="secureRandom" ref="secureRandom"/> 
</bean> 


<bean name="secureRandom" class="java.security.SecureRandom"> 
    <property name="seed" value="122"/> 
</bean> 

Bên cạnh đó bạn cũng cần phải

sử dụng một PreAuthenticatedAuthenticationProvider

entry-point-ref thuộc tính của <http> để Http403ForbiddenEntryPoint đậu

+0

Tôi rất quan tâm đến việc xác thực dựa trên mã thông báo này nhưng không hiểu mối quan hệ với PreAuthenticatedAuthenticationProvider ... không phải mã thông báo cần phải được xác minh để xác thực không? ... bạn có thể giải thích thêm một chút nữa không? – Rafael

1

Tôi đã tìm thấy một cách dễ dàng hơn:

Giải pháp của tôi hoạt động để xác thực mã thông báo và xác thực biểu mẫu, nhưng bạn có thể dễ dàng tắt một trong số chúng nếu muốn.

Bộ lọc của tôi tương tự bộ lọc của La Mã, nhưng tôi không cần kiểm tra xem người dùng có quyền truy cập vào tài nguyên cụ thể hay không, cũng không có đăng xuất được xử lý -> được chuyển đến springSecurity.

lọc auth:

public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter { 

private static final String SECURITY_TOKEN_KEY = "token"; 
private static final String SECURITY_TOKEN_HEADER = "X-Token"; 

public TokenAuthenticationFilter() { 

    super("/"); 
} 

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

    HttpServletRequest request = (HttpServletRequest) req; 
    HttpServletResponse response = (HttpServletResponse) res; 

    String token = request.getParameter(SECURITY_TOKEN_KEY); 
    // or this.token = request.getHeader(SECURITY_TOKEN_HEADER); 

    if (token != null) { 

     Authentication authResult; 
     try { 
      authResult = attemptAuthentication(request, response, token); 
      if (authResult == null) { 
       notAuthenticated(request, response, new LockedException("User Not found")); 
       return; 
      } 
     } catch (AuthenticationException failed) { 
      notAuthenticated(request, response, failed); 
      return; 
     } 

     try { 
      successfulAuthentication(request, response, chain, authResult); 
      return; 
     } catch (NestedServletException e) { 
      logger.error(e.getMessage(), e); 
      if (e.getCause() instanceof AccessDeniedException) { 
       notAuthenticated(request, response, new LockedException("Forbidden")); 
       return; 
      } 
     } 
    } 
    chain.doFilter(request, response);// return to others spring security filters 
} 

public void notAuthenticated(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException { 

    response.sendRedirect("http://www.google.ro"); 
    // unsuccessfulAuthentication(request, response, failed); 
} 

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response, String token) throws AuthenticationException, IOException, ServletException { 

    AbstractAuthenticationToken userAuthenticationToken = authUserByToken(token); 
    if (userAuthenticationToken == null) 
     throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token")); 

    return userAuthenticationToken; 
} 

private AbstractAuthenticationToken authUserByToken(String tokenRaw) { 

    AbstractAuthenticationToken authToken = null; 
    try { 
     // check your input token, identify the user 
     // if success create AbstractAuthenticationToken for user to return 
     // eg: 
     // authToken = new UsernamePasswordAuthenticationToken(username, userHash, userAuthorities); 
     // authToken = new UsernamePasswordAuthenticationToken(tokenRaw, authToken,) 
     logger.info("token received = " + tokenRaw); 
     // obtain user by your methods 
     // if (user != null) { 
     // SecurityUser securityUser = new SecurityUser(user); 
     // return new PreAuthenticatedAuthenticationToken(securityUser, securityUser.getPassword(), securityUser.getAuthorities()); 
     // } 
    } catch (Exception e) { 
     logger.error("Error during authUserByToken", e); 
    } 
    return authToken; 
} 

@Override 
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException { 

    SecurityContextHolder.getContext().setAuthentication(authResult); 

    new CustomAuthenticationSuccessHandler().onAuthenticationSuccess(request, response, authResult); 
} 

@Override 
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { 

    logger.error("No TOKEN PROVIDED"); 
    return null; 
    } 

} 

sau đó tất cả các bạn phải làm gì để lập bản đồ bộ lọc này được cấu hình nó trong springSecurity (addFilterBefore), điều này không phải được ánh xạ trong servlet config.

 http.authorizeRequests().antMatchers("/login*").permitAll(); 
     http.authorizeRequests().antMatchers("/register*").permitAll(); 

     http.authorizeRequests().antMatchers("/admin/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER");// 

     http.authorizeRequests().and().formLogin()// 
       .loginPage("/login")// 
       .successHandler(successHandler())// 
       .failureUrl("/login?error").permitAll()// 
       .and().logout()// 
       .logoutUrl("/logout").logoutSuccessUrl("/login?logout").permitAll()// 
       .and().rememberMe().key(applicationName + "_key").tokenValiditySeconds(2419200); // remember me for 2 weeks 

     http.addFilterBefore(new TokenAuthenticationFilter(), AnonymousAuthenticationFilter.class); 
+0

Tôi có một câu hỏi về tất cả điều này? Khi một mã thông báo được tạo ra trên backend, cái gì tạo ra mã thông báo, và nó được lưu trữ/lưu giữ như thế nào trên backend? Bao lâu là nó? Và khi chúng tôi nhận được yêu cầu AJAX cho chương trình phụ trợ, với mã thông báo ... Tôi thấy chúng tôi có một số mã để tra cứu mã thông báo đó từ kho lưu trữ mã thông báo, đúng không?Khi mã thông báo được tạo trên phần phụ trợ và được lưu trữ, khi chúng tôi gửi mã thông báo cho giao diện người dùng, bạn có lưu trữ mã đó dưới dạng cookie không? Điều gì sẽ xảy ra nếu cookie bị tắt? Cảm ơn! – tjholmes66

Các vấn đề liên quan