我正在开发一款新的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中添加一些类来检查令牌。
TXS人。这帮助我摆脱了这个错误。现在我有其他错误。但为什么我必须这样做? – java4fun
你的错误是什么? – shazin
嗨,我已更正来源和错误。现在问题出现在我登录后,我变成了access_token,但我不知道要在哪里验证令牌。 – java4fun