2015-12-01 80 views
2

我正在开发一款新的REST服务器,它是用Spring开发的。服务器必须只有服务器端逻辑,没有任何JS或视图。Spring Security + spring oauth2分辨率

目前我开始使用Spring引导版本1.2.7,但这只是为了给服务器给前端开发者,直到服务器安装完成。

之后我将更改为spring-core版本4.1或类似的东西。 目前我很难配置安全部分。

在开始时我开始使用Java Config配置,但在更改为xml之后,因为我已经有了类似的配置。

我的最终结果必须是这些:

hostname **/api/auth**** :is the entry point where a frontend developer make a **POST request with username and password of a customer. This call returns a token. This token permits me to identify the user the next time.

hostname /api/secure/resource/1 : is a resource that is protected, and can be accessible only with a valid token

hostname /api/other/1 : is an other type of resource that is not protected and can be accessible for everyone hostname /api/secure/bambi/: is a resource that can be accessed from everyone but if it has a token then, more object-parameters are shown.

这接缝对我来说是相对简单的配置,但我不能配置它。 我知道这不是他的工作,但是为了处理令牌,并且我将使用OAUTH2基础结构(我知道,这可以做得更好,但这是必不可少的)资源访问

如下我给你写我的配置:

StartUpApplication.java

@SpringBootApplication(exclude = DispatcherServletAutoConfiguration.class) 
@Import({ InMemoryDBConfigurationImpl.class}) 
@ImportResource({ "classpath:config/security-context.xml" }) 
public class SalustroApplication { 

@Autowired 
@Qualifier("InMemoryConfig") 
private SystemConfiguration systemConfiguration; 

public static void main(String[] args) { 
    SpringApplication app = new SpringApplication(SalustroApplication.class); 
    app.run(args); 
} 

@Bean 
public ServletRegistrationBean foo() { 
    DispatcherServlet dispatcherServlet = new DispatcherServlet(); 
    AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); 
    applicationContext.register(FooConfig.class); 
    dispatcherServlet.setApplicationContext(applicationContext); 
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/"); 
    servletRegistrationBean.setName("foo"); 
    return servletRegistrationBean; 
} 

我需要为安全部分foo的方法?

安全的context.xml

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

<sec:http pattern="/api/auth" create-session="stateless" 
    authentication-manager-ref="clientAuthenticationManager" use-expressions="true"> 
    <sec:intercept-url pattern="/api/auth" access="IS_AUTHENTICATED_FULLY" /> 
    <sec:anonymous enabled="false" /> 
    <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" 
     after="BASIC_AUTH_FILTER" /> 
    <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> 
    <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" /> 
    <sec:csrf disabled="true" /> 
</sec:http> 

<sec:http pattern="/api/**" create-session="never" 
    entry-point-ref="oauthAuthenticationEntryPoint" use-expressions="true" 
    access-decision-manager-ref="accessDecisionManager"> 
    <sec:intercept-url pattern="/api/**" 
     access="isFullyAuthenticated() AND hasRole('ROLE_USER')" /> 
    <sec:anonymous enabled="false" /> 
    <sec:custom-filter ref="resourceServerFilter" 
     before="PRE_AUTH_FILTER" /> 
    <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> 
    <sec:csrf disabled="true" /> 
    <sec:headers /> 
</sec:http> 

<sec:authentication-manager id="clientAuthenticationManager"> 
    <sec:authentication-provider 
     user-service-ref="clientDetailsUserService" /> 
</sec:authentication-manager> 

<bean id="accessDecisionManager" 
    class="org.springframework.security.access.vote.UnanimousBased" 
    c:decisionVoters-ref="votersList" /> 

<bean id="clientAuthenticationEntryPoint"  class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" 
    p:realmName="p4me-test/client" p:typeName="Basic" /> 

<bean id="clientCredentialsTokenEndpointFilter" 
    class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter" 
    p:authenticationManager-ref="clientAuthenticationManager" 
    p:filterProcessesUrl="/api/auth" /> 
<bean id="clientDetailsService" 
    class="app.security.ClientDetailsServiceImpl" /> 
<bean id="clientDetailsUserService" 
    class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService" 
    c:clientDetailsService-ref="clientDetailsService" /> 

<bean id="clientDetailServiceImpl" class="app.security.ClientDetailsServiceImpl" /> 

<bean id="oauthAccessDeniedHandler" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> 
<bean id="oauthAuthenticationEntryPoint" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" 
    p:realmName="p4me-test"> 
</bean> 

<oauth:resource-server id="resourceServerFilter" 
    resource-id="test" token-services-ref="tokenServices" /> 

<bean id="tokenEnhancer" class="app.security.CustomTokenEnhancer" /> 
<bean id="tokenServices" class="app.security.CustomTokenServices" 
    p:tokenStore-ref="tokenStore" p:clientDetailsService-ref="clientDetailsService" 
    p:supportRefreshToken="true" p:tokenEnhancer-ref="tokenEnhancer" 
    p:accessTokenValiditySeconds="1800" /> 

<bean id="tokenStore"  class="org.springframework.security.oauth2.provider.token.store.InMemory TokenStore" /> 
    <sec:authentication-manager alias="authenticationManager"> 
    <sec:authentication-provider ref="userAuthenticationProvider" /> 
</sec:authentication-manager> 
<bean id="userAuthenticationProvider" 
    class="app.config.impl.security.SecureAuthenticationProvider"/> 

<oauth:authorization-server 
    client-details-service-ref="clientDetailsService" 
    token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler" 
    token-endpoint-url="/api/auth"> 
    <oauth:authorization-code /> 
    <oauth:implicit /> 
    <oauth:refresh-token /> 
    <oauth:client-credentials /> 
    <oauth:password /> 
</oauth:authorization-server> 

<bean id="userApprovalHandler" 
    class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler" 
    p:tokenStore-ref="tokenStore" p:requestFactory-ref="requestFactory" /> 

<bean id="requestFactory" 
    class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory" 
    c:clientDetailsService-ref="clientDetailServiceImpl" /> 

<util:list id="votersList"> 
    <bean class="app.security.AccessVoter" /> 
    <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" /> 
    <bean class="org.springframework.security.access.vote.RoleVoter" /> 
    <bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> 
    <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"> 
     <property name="expressionHandler"> 
      <bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" /> 
     </property> 
    </bean> 
</util:list> 

测试类

@WebAppConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = SalustroApplication.class) 
public class AuthTest { 

@Autowired 
private WebApplicationContext context; 

@Autowired 
private Filter springSecurityFilterChain; 

@Test 
public void find1() throws Exception { 
    ResultActions doLogin = doLogin(); 
    String contentAsString = doLogin.andReturn().getResponse().getContentAsString(); 
    JSONObject json = new JSONObject(contentAsString); 

    DefaultMockMvcBuilder webAppContextSetup = MockMvcBuilders.webAppContextSetup(context) 
      .addFilter(springSecurityFilterChain); 
    MockMvc build = webAppContextSetup.build(); 
    final ResultActions userResult = build.perform(post("/api/secure/user/1") 
      .param("access_token", json.getString("access_token")).accept(MediaType.APPLICATION_JSON)) 
      .andDo(print()); 

    assertEquals(someUser, userResult); 


} 

protected ResultActions doLogin() throws Exception { 
    DefaultMockMvcBuilder webAppContextSetup = MockMvcBuilders.webAppContextSetup(context) 
      .addFilter(springSecurityFilterChain); 
    MockMvc build = webAppContextSetup.build(); 

    final ResultActions loginResult = build.perform(post("/api/auth").param("grant_type", "password") 
      .param("client_id", "testId").param("client_secret", "testSecret").param("username", "someUser") 
      .param("password", "somePassword").param("scope", "read").accept(MediaType.APPLICATION_JSON)).andDo(print()); 

    return loginResult; 
} 

}

SecureAuthenticationPr iver.class

@Component 
public class SecureAuthenticationProvider implements AuthenticationProvider { 

protected final static Logger logger = LoggerFactory.getLogger(SecureAuthenticationProvider.class); 

@Override 
public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
    String name = authentication.getName(); 
    String password = authentication.getCredentials().toString(); 
    List<GrantedAuthority> grantedAuths = new ArrayList<>(); 

    GrantedAuthority authorithy = new SimpleGrantedAuthority("USER"); 
    grantedAuths.add(authorithy); 

    UserEntity authenticatedUser = userPersistence.findByUserName(name, password); 
    if (authenticatedUser != null) { 
     return new UsernamePasswordAuthenticationToken(name, password, grantedAuths); 
    } else 
     return null; } 

@Override 
public boolean supports(Class<?> authentication) { 
    return false; 
} 
} 

AccessVoter.class

@Service 
public class AccessVoter implements AccessDecisionVoter<Object> { 
@Override 
public boolean supports(final ConfigAttribute attribute) { 
    return true; 
} 

@Override 
public boolean supports(final Class<?> clazz) { 
    return true; 
} 

@Override 
@Transactional 
public int vote(final Authentication authentication, final Object object, 
     final Collection<ConfigAttribute> attributes) { 
    final Object principal = authentication.getPrincipal(); 
    return 1; 
} 

private int refreshUserDetails(final Principal principal) { 
    return 1; 
} 
} 

ClientDetailServiceImpl.class

public class ClientDetailsServiceImpl implements ClientDetailsService { 

@Override 
public ClientDetails loadClientByClientId(final String clientId) { 
    if ("invalid".equals(clientId)) { 
     throw new ClientRegistrationException(clientId + " not found"); 
    } 
    return createClientDetails(clientId); 
} 

private ClientDetails createClientDetails(final String clientId) { 
    final Set<GrantedAuthority> grantAuthorities = new HashSet<GrantedAuthority>(); 
    grantAuthorities.add(new SimpleGrantedAuthority("ROLE_USER")); 

    final Set<String> authorizedGrantTypes = new HashSet<String>(); 
    authorizedGrantTypes.add("password"); 
    final BaseClientDetails details = new BaseClientDetails(); 
    details.setClientId("testId"); 
    details.setClientSecret("testSecret"); 
    details.setAuthorizedGrantTypes(authorizedGrantTypes); 
    details.setAuthorities(grantAuthorities); 
    return details; 
} 
} 

CustomTokenEnhancer.class

@Component 
public class CustomTokenEnhancer implements TokenEnhancer { 

private List<TokenEnhancer> delegates = Collections.emptyList(); 

@Autowired 
private UserService userService; 

public void setTokenEnhancers(final List<TokenEnhancer> delegates) { 
    this.delegates = delegates; 
} 

@Override 
public OAuth2AccessToken enhance(final OAuth2AccessToken accessToken, final OAuth2Authentication authentication) { 
    final DefaultOAuth2AccessToken tempResult = (DefaultOAuth2AccessToken) accessToken; 
    // tempResult.setAdditionalInformation(getAuthenticationMethod(authentication)); 
    OAuth2AccessToken result = tempResult; 
    for (final TokenEnhancer enhancer : delegates) { 
     result = enhancer.enhance(result, authentication); 
    } 
    return result; 
} 

private boolean isAdmin(final Collection<GrantedAuthority> authorities) { 
    for (final GrantedAuthority grantedAuthority : authorities) { 
     if (grantedAuthority.getAuthority().compareTo("ROLE_ADMIN") == 0) { 
      return true; 
     } 
    } 
    return false; 
} 
} 

CustomTokenServices。类

public class CustomTokenServices extends DefaultTokenServices { 

private TokenStore tokenStore; 

private ClientDetailsService clientDetailsService; 

private TokenEnhancer accessTokenEnhancer; 

@Override 
public void afterPropertiesSet() throws Exception { 
    Assert.notNull(tokenStore, "tokenStore must be set"); 
} 

@Override 
public OAuth2AccessToken createAccessToken(final OAuth2Authentication authentication) { 

    final OAuth2AccessToken existingAccessToken = tokenStore 
      .getAccessToken(authentication); 
    OAuth2RefreshToken refreshToken = null; 
    if (existingAccessToken != null && existingAccessToken.isExpired()) { 
     if (existingAccessToken.getRefreshToken() != null) { 
      refreshToken = existingAccessToken.getRefreshToken(); 
      tokenStore.removeRefreshToken(refreshToken); 
     } 
     tokenStore.removeAccessToken(existingAccessToken); 
    } 

    refreshToken = createRefreshToken(authentication); 
    final ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; 
    if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { 
     refreshToken = createRefreshToken(authentication); 
    } 

    final OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); 
    tokenStore.storeAccessToken(accessToken, authentication); 
    refreshToken = accessToken.getRefreshToken(); 
    if (refreshToken != null) { 
     tokenStore.storeRefreshToken(refreshToken, authentication); 
    } 
    return accessToken; 

} 

@Override 
public OAuth2Authentication loadAuthentication(final String accessTokenValue) { 
    final DefaultOAuth2AccessToken accessToken = (DefaultOAuth2AccessToken) tokenStore 
      .readAccessToken(accessTokenValue); 
    if (accessToken == null) { 
     throw new InvalidTokenException("Invalid access token: " + accessTokenValue); 
    } else if (accessToken.isExpired()) { 
     tokenStore.removeAccessToken(accessToken); 
     throw new InvalidTokenException("Access token expired: " + accessTokenValue); 
    } 

    final OAuth2Authentication result = tokenStore 
      .readAuthentication(accessToken); 

    if (clientDetailsService != null) { 
     final String clientId = result.getOAuth2Request().getClientId(); 
     try { 
      clientDetailsService.loadClientByClientId(clientId); 
     } catch (final ClientRegistrationException e) { 
      throw new InvalidTokenException("Client not valid: " + clientId, e); 
     } 
    } 
    final int validitySeconds = getAccessTokenValiditySeconds(result 
      .getOAuth2Request()); 
    accessToken 
      .setExpiration(new Date(System.currentTimeMillis() + validitySeconds * 1000L)); 
    return result; 
} 

private ExpiringOAuth2RefreshToken createRefreshToken(final OAuth2Authentication authentication) { 
    if (!isSupportRefreshToken(authentication.getOAuth2Request())) { 
     return null; 
    } 
    final int validitySeconds = getRefreshTokenValiditySeconds(authentication 
      .getOAuth2Request()); 
    final ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(UUID 
      .randomUUID().toString(), new Date(System.currentTimeMillis() + validitySeconds * 1000L)); 
    return refreshToken; 
} 

private OAuth2AccessToken createAccessToken(final OAuth2Authentication authentication, final OAuth2RefreshToken refreshToken) { 
    final DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID 
      .randomUUID().toString()); 
    final int validitySeconds = getAccessTokenValiditySeconds(authentication 
      .getOAuth2Request()); 
    if (validitySeconds > 0) { 
     token.setExpiration(new Date(System.currentTimeMillis() + validitySeconds * 1000L)); 
    } 
    token.setRefreshToken(refreshToken); 
    token.setScope(authentication.getOAuth2Request().getScope()); 

    return accessTokenEnhancer != null ? accessTokenEnhancer 
      .enhance(token, authentication) : token; 
} 

@Override 
public void setTokenEnhancer(final TokenEnhancer accessTokenEnhancer) { 
    this.accessTokenEnhancer = accessTokenEnhancer; 
} 

@Override 
public void setTokenStore(final TokenStore tokenStore) { 
    this.tokenStore = tokenStore; 
} 

@Override 
public void setClientDetailsService(final ClientDetailsService clientDetailsService) { 
    this.clientDetailsService = clientDetailsService; 
} 

} 

如何我在开始时说:我从配置的其他工作副本开始,并转化到这个应用程序的需要。 这也有可能让我混淆了一些配置。

我重复最后我会利用OAUTH2系统生成的令牌,并利用此令牌的用户进行身份验证。该认证是在与/api/auth作出(或/api/secure/auth?),资源是/api/secure下仅适用于具有有效凭证和其他资源的用户/api/yyy下可用,如果他们有一个令牌返回

当我运行的详细信息测试要尽量做到拿我收到此错误的资源:

Body = {"error":"access_denied","error_description":"Access is denied"} 

现在我不知道exactely在那里我有工作。在security-context.xml中添加一些类来检查令牌。

回答

1

例外

Body = {"error":"unauthorized","error_description":"There is no client authentication. Try adding an appropriate authentication filter."} 

实际上与AuthTest测试类,其中WebApplicationContext中不包含Spring Security的过滤器链。您需要对AuthTest测试课进行以下更改。

@WebAppConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = SalustroApplication.class) 
public class AuthTest { 

    @Autowired 
    private WebApplicationContext context; 

    // Inject this 
    @Autowired 
    private Filter springSecurityFilterChain; 

    @Test 
    public void testLogin() throws Exception { 
     ResultActions doLogin = doLogin(); 
     assertEquals(doLogin.andReturn().getResponse().getContentAsString(), "A valid Token"); 
    } 

    protected ResultActions doLogin() throws Exception { 
     DefaultMockMvcBuilder webAppContextSetup = MockMvcBuilders.webAppContextSetup(context).addFilter(springSecurityFilterChain); // Add filter 
     MockMvc build = webAppContextSetup.build(); 

     final ResultActions loginResult = build.perform(post("/api/auth").param("grant_type", "password") 
       .param("client_id", "testId").param("client_secret", "testSecret").param("username", "aUserName") 
       .param("password", "123456").accept(MediaType.APPLICATION_JSON)).andDo(print()); 

     return loginResult; 

    } 
} 
+0

TXS人。这帮助我摆脱了这个错误。现在我有其他错误。但为什么我必须这样做? – java4fun

+0

你的错误是什么? – shazin

+0

嗨,我已更正来源和错误。现在问题出现在我登录后,我变成了access_token,但我不知道要在哪里验证令牌。 – java4fun

相关问题