2015-12-01 2 views
2

Я работаю над новым сервером REST, который я разрабатываю с весной. Сервер должен иметь только логику на стороне сервера, ничего js или views.Весна Безопасность + весна oauth2 разрешение

В данный момент я начал с Spring-boot version 1.2.7, но это только для того, чтобы предоставить серверу разработчикам интерфейсов до тех пор, пока настройка сервера не будет выполнена.

После того, как я перейду на версию весеннего ядра 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 инфраструктуры (I'know, что это может быть сделано лучше, но это было необходимым условием)

Как следовать Я пишу вам свою конфигурацию:

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 man. Это помогло мне уйти от этой ошибки. Теперь у меня другая ошибка. Но почему я должен это делать? – java4fun

+0

Какая ваша ошибка? – shazin

+0

Привет, я исправил источники и ошибку. Теперь проблемы возникают после того, как я сделал логин, я стал access_token, но я не знаю, где мне нужно проверить токен. – java4fun

Смежные вопросы