I have my authorization server working with the autoconfiguration of spring-oauth2 #EnableAuthorizationServer in which I have my own custom ClientDetailService service, I am doing very well but I have a problem, it turns out that said service runs 6 times when invoking the endpoint [ oauth / token], which I am not sure is normal behavior but I want it to be executed only once because in it I call my database. Please your support.
My configuration:
#Configuration
#EnableAuthorizationServer
#Slf4j
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
#Autowired
private ApplicationJwtAccessTokenConverter applicationJwtAccessTokenConverter;
#Autowired
private ApplicationOauth2Service applicationOauth2Service;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(applicationOauth2Service);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenEnhancer(applicationJwtAccessTokenConverter)
.tokenStore(jwtTokenStore());
}
#Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(applicationJwtAccessTokenConverter);
}
}
My ClientDetailService:
#Service
#Slf4j
public class ApplicationOauth2Service implements ClientDetailsService {
#Autowired
UserRepository userRepository;
User user;
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
BaseClientDetails details = new BaseClientDetails();
details.setAuthorizedGrantTypes(Collections.singletonList("client_credentials"));
user = userRepository.findByEmail(clientId);
if (null == user) {
throw new NoSuchClientException("No client with requested id: " + clientId);
}
details.setClientId(user.getAuth().getEmail());
details.setClientSecret(user.getAuth().getPassword());
return details;
}
}
My console output:
Related
I use spring-security-oauth2.
I would like to custom configure the /oauth/token end-point.
After registration a new client, I need to use login and password from this client and create a token for him and return this token.
Can I custom configure this /oauth/token endpoint?
Can I return a custom error if I receive incorrect information for this end-point (incorrect login or password)?
Can I put here a client-id and client-secret and don`t enter this in front-end?
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore tokenStore;
#Autowired
private JwtAccessTokenConverter jwtTokenEnhancer;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).tokenEnhancer(jwtTokenEnhancer).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
}
I using ConnectionSignUp and SignInAdapter to do login function for website. I also use spring security.But I do not know how to finish loading the current page after logging in.
After login, the website will reload the form page : localhost:9090/#=
ConnectionSignUp.java
public class FacebookSignInAdapter implements SignInAdapter {
#Autowired
UsersRepository usersService;
#Override
public String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(connection.getKey(),
null, Arrays.asList(new SimpleGrantedAuthority("ROLE_FACEBOOK"))));
return null;
}}
FacebookConnectionSignup.java
public class FacebookConnectionSignup implements ConnectionSignUp {
#Autowired
UsersService usersService;
#Autowired
RolesService rolesService;
#Override
public String execute(Connection<?> connection) {
Users user = null;
try {
user = usersService.findByUserName(connection.getKey().toString());
if(user == null ) {
user = new Users();
user.setUserName(connection.getKey().toString());
user.setPassword(randomAlphabetic(8));
user.setEmail(connection.getKey()+"#gmail.com");
user.setFirstName(connection.getDisplayName());
user.setAvatar(connection.getImageUrl());
user.setStatus("active");
user.setCreatedDate(new Date());
user.setLoggedInDate(new Date());
user.setIsOnline((byte) 1);
HashSet<Roles> roleses = new HashSet<>();
roleses.add(rolesService.findByName("ROLE_FACEBOOK"));
user.setRoleses(roleses);
usersService.saveorupdate(user);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return user.getUserName();
}}
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;
#Autowired
private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
#Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
#Autowired
private UsersConnectionRepository usersConnectionRepository;
#Autowired
private FacebookConnectionSignup facebookConnectionSignup;
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/home", "/").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.and().formLogin().loginPage("/403").loginProcessingUrl("/login").usernameParameter("userName").passwordParameter("password")
.failureHandler(ajaxAuthenticationFailureHandler).successHandler(ajaxAuthenticationSuccessHandler)
.and().logout().logoutSuccessUrl("/")
.and().rememberMe().and()
.exceptionHandling().accessDeniedPage("/403");
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
#Bean
public ProviderSignInController providerSignInController() {
((InMemoryUsersConnectionRepository) usersConnectionRepository).setConnectionSignUp(facebookConnectionSignup);
return new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository,
new FacebookSignInAdapter());
}}
Now I want to finish loading the current page after logging in.
Hope everyone will help.
Thank you very much.
I'm facing an issue authenticating my app under junit-test.
I've got a CustomAuthenticationProvider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserRepository userRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//businness logic
return auth;
}
}
A SecurityConfig, that uses it
#Configuration
#EnableWebSecurity
#Import(CustomAuthenticationProvider.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//some permission filters here
}
}
And my test, that is supposed to call on a Rest API and make sure, that answer is Ok.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#SpringApplicationConfiguration(classes = {MyApplication.class},locations = {"/dbContext.xml"})
#TestPropertySource("/application.properties")
#WebIntegrationTest
public class SimpleTest {
#Autowired
protected WebApplicationContext webAppContext;
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(webAppContext);
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication user = customAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken("admin", "123"));
context.setAuthentication(user);
SecurityContextHolder.setContext(context);
}
#Test
public void makeSureLoginIsOk() {
given().when().get("/myurl").then().statusCode(200);
}
}
Well, the test always failes, because GET returns 401, instead of 200.
Can anyone help, what it wrong with SecurityContext?
Finally found an answer:
This post How to Mock the security context in Spring MVC for testing was helpfull
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(webAppContext);
RestAssuredMockMvc.enableLoggingOfRequestAndResponseIfValidationFails();
Authentication user = customAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken("admin", "123"));
RestAssuredMockMvc.authentication(user); //add this
SecurityContextHolder.getContext().setAuthentication(user);
}
#Test
public void makeSureLoginIsOk() {
RestAssuredMockMvc.get("/myurl").then().statusCode(200); //change given to RestAssured
}
I'm slowly moving into understanding Spring Cloud Security. I've created an authorization service and it works when authorizing and returning a token, but doesn't return any current user details when using that token, when getting these from OAuth2Authentication. This two lines return an NPE:
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
OAuth2Authentication user isn't instantiated and is null, while I understood that it should be by default instantiated by Spring Security. Maybe I am missing some configuration beans? Thanks in advance!
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class AuthorizationServiceApplication {
#RequestMapping(value = {"/user"}, produces = "application/json")
public Map <String, Object> user (OAuth2Authentication user) {
Map <String, Object> userInfo = new HashMap <>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.class
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Value("${token.secret}")
private String secret;
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
public OAuth2Config (AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.class
#Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean () throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
// TODO: implemented DB stuff
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("deniss").password("deniss1").roles("USER")
.and()
.withUser("oksana").password("oksana").roles("USER, ADMIN");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository());
}
}
In the end I got it working like this:
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
public class AuthorizationServiceApplication {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping("/user")
public Principal user(Principal user) {
log.info("User information display for User: " + user.getName());
return user;
}
#Bean
UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("deniss").password("deniss").roles("USER").build());
return manager;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
//TODO: refactor to recieve this info from config server
#Value("${token.secret}")
private String secret;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}
SecurityConfigurer.class
#Configuration
#EnableGlobalAuthentication
public class SecurityConfigurer extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
// TODO: implemented DB stuff
#Override
public void init(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
}
I met the same problem, it seems a bug of new version. I changed Spring Boot 1.5.9.RELEASEļ¼Spring Cloud Edgware.RELEASE backed to Spring Boot 1.4.4.RELEASEļ¼Spring Cloud Camden.SR5, the problem disappeared.
Set security.oauth2.resource.filter-order=3 configuration property to restore the ordering used in previous versions. See enter link description here for more details.
I have a Spring Boot project in which I've configured a Spring OAuth2 authentication process which partially works. I can authenticate OK but when I'm trying to get a refresh token I get an exception.
OAuth configuration:
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "xxx";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Value("${clientDetailsService.clientName}")
private String clientName;
#Value("${clientDetailsService.clientSecret}")
private String clientSecret;
#Autowired
#Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
#Qualifier("tokenServices")
private AuthorizationServerTokenServices tokenServices;
#Autowired
#Qualifier("codeServices")
private AuthorizationCodeServices codeServices;
#Autowired
#Qualifier("requestFactory")
private OAuth2RequestFactory requestFactory;
#Autowired
#Qualifier("tokenGranter")
private TokenGranter tokenGranter;
private final TokenStore tokenStore = new InMemoryTokenStore();
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.setClientDetailsService(clientDetailsService);
endpoints.tokenServices(tokenServices)
.tokenStore(tokenStore)
.authorizationCodeServices(codeServices)
.authenticationManager(authenticationManager)
.requestFactory(requestFactory)
.tokenGranter(tokenGranter);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Bean(name = "tokenGranter")
#Primary
public TokenGranter tokenGranter() {
final List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, codeServices, clientDetailsService, requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
return new CompositeTokenGranter(tokenGranters);
}
#Bean
#Primary
public ClientDetailsService clientDetailsService(){
final InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder();
builder.withClient(clientName)
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret(clientSecret);
try {
return builder.build();
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
#Bean(name = "tokenServices")
#Primary
public AuthorizationServerTokenServices tokenServices() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setTokenStore(tokenStore);
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
#Bean(name = "requestFactory")
#Primary
public OAuth2RequestFactory requestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
#Bean(name = "codeServices")
#Primary
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
}
I also have some custom components defined, like a custom Token Granter, custom authentication provider etc. I'll post them if necessary.
As I said, authentication flow works OK. When I POST to /oauth/token I get a token and a refresh token, but when I then try to exchange my refresh token for a new token (by POSTing http://localhost:8080/oauth/token with grant_type=refresh_token and refresh_token=my refresh token) I get an exception:
No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
Where do I set the authentication provider? How do I get Spring to use my custom authentication provider for refresh tokens also?
I fixed this by explicitly defining a PreAuthenticationProvider:
#Component("preAuthProvider")
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider {
#Autowired
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userService;
public CustomPreAuthProvider(){
super();
}
#PostConstruct
public void init(){
super.setPreAuthenticatedUserDetailsService(userService);
}
}
and then a custom userservice:
#Service
public class CustomPreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Override
public final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) {
...
}
}
and adding this provider to the oauth2 config:
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("preAuthProvider")
private AuthenticationProvider preAuthProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider).authenticationProvider(preAuthProvider);
}
As an alternative way to Jesper's answer, if you want to reuse your current UserDetailsService for this purpose, you can just do it the same way as Spring does it with their DefaultTokenServices:
#Bean
public CustomTokenServices tokenServices() {
CustomTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setAuthenticationManager(createPreAuthProvider());
return tokenServices;
}
private ProviderManager createPreAuthProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
return new ProviderManager(Arrays.asList(provider));
}
UserDetailsService is autowired into this #Configuration class.
I can not be disagree with Jesper's answer, but in my case, that same error has been fixed removing:
tokenServices.setAuthenticationManager(authenticationManager)
from tokenService()