Ok guys, I hope you can help me, this is my last attempt. I am quite new to this spring security world and I cant get this to work. I tried many things, followed many tutorials and nothing.
The problem is as you saw in the title, make a custom user details service to work. It just not logs in, It appears that the customuserdetailsservice is not being called, as the sysouts are not showing in the console...
It works as a charm with spring security in memory features. Below are my codes.
Spring Security Config:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication().withUser("ram").password("ram123").roles("ADMIN");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/bower_components/**", "/resources/**", "/img/**"); // #3
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/call").permitAll() // #4
.antMatchers("/resource", "/video").hasRole("USER") // #6
.anyRequest().authenticated();
}
#Bean(name="passwordEncoder")
public PasswordEncoder passwordencoder(){
return new BCryptPasswordEncoder();
}
}
CustomUserDetailsService
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
private UserService userService;
#Override
public UserDetails loadUserByUsername(String ssoId)
throws UsernameNotFoundException {
User user = userService.findByUsername(ssoId);
System.out.println("User : "+user);
if(user==null){
System.out.println("User not found");
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
user.isEnabled(), true, true, true, getGrantedAuthorities(user));
}
private List<GrantedAuthority> getGrantedAuthorities(User user){
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(user.getRole()));
System.out.print("authorities :"+authorities);
return authorities;
}
}
Initializer Class
#SpringBootApplication
#EnableWebSocket
public class One2OneCallApp implements WebSocketConfigurer {
#Bean
public CallHandler callHandler() {
return new CallHandler();
}
#Bean
public UserRegistry registry() {
return new UserRegistry();
}
#Bean
public KurentoClient kurentoClient() {
return KurentoClient.create();
}
#Bean
public UiApplication uiApplication(){
return new UiApplication();
}
#Bean
public CustomUserDetailsService customUserDetailsService(){
return new CustomUserDetailsService();
}
#Bean
public SecurityConfig securityConfig(){
return new SecurityConfig();
}
#Bean
public EncryptPassword encryptPassword(){
return new EncryptPassword();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(callHandler(), "/call");
}
public static void main(String[] args) throws Exception {
System.out.println("Iniciando");
new SpringApplication(One2OneCallApp.class).run(args);
}
}
I've also tested the communication with the database and it works perfectly fine. I'm seeking any help. Sorry for bad English. Thank you all!
Edit: Answered my own question down below.
In SecurityConfig class:
#Autowired
CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
Change
#Autowired
UserDetailsService userDetailsService;
to
#Autowired
CustomUserDetailsService userDetailsService;
Also, import the security config in you web/socket config and move the component scan there, not on the security
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
#Import(value = { SecurityConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter { /*...*/ }
You are setting hasRole(" ") in security config and you are using authorities for authentication.
instead of using .antMatchers("/resource", "/video").hasRole("USER")
use .antMatchers("/resource", "/video").hasAuthority("USER")
I ended up staying with the built in memory anthentication just for the presentation I had to do. I think my problem had to do with something in spring boot and the initialization in my application.
Related
I am trying to implement authentication with Spring Boot.
When I run the application and try to login I realized, until that time, configure methods of WebSecurityConfigurerAdapter extending class never called. Also I cant see the behaviors of them during authentication or limiting url access.
Despite googling & examination, I can't figure out what I have missed with standard implementation. Thanks in advance.
package com.sampleapp;
#Log
#SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
})
#ComponentScan({ "com.sampleapp.controller", "com.sampleapp.business" })
public class SampleBootProjectApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SampleBootProjectApplication.class, args);
}
.
.
}
Here is WebSecurityConfigurerAdapter :
package com.sampleapp.security;
#Log
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Resource
private CustomUserDetailService userDetailsService;
#Autowired
private CustomAuthenticationProvider customAuthProvider;
#Value("${authentication.type}")
private Integer authenticationType;
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("configure(HttpSecurity http) called");
.
.
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("configure(AuthenticationManagerBuilder auth) called");
if(authenticationType == null) {
System.out.println("Invalid authenticationType property. It is null");
}
if(authenticationType == 1) {
auth.ldapAuthentication()
.contextSource()
.url(ldapUrls + ldapBaseDn)
.managerDn(ldapSecurityPrincipal)
.managerPassword(ldapPrincipalPassword)
.and()
.userDnPatterns(ldapUserDnPattern);
} else if(authenticationType == 2) {
auth.inMemoryAuthentication()
.withUser("employee").password("employee").authorities("ROLE_USER").and()
.withUser("javainuse").password("javainuse").authorities("ROLE_USER", "ROLE_ADMIN");
} else if(authenticationType == 3) {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(new BCryptPasswordEncoder());
auth.authenticationProvider(authProvider);
} else if(authenticationType == 4) {
auth.authenticationProvider(customAuthProvider);
} else {
System.out.println("Invalid authenticationType property: " + authenticationType);
}
}
Found it.
Despite SecurityConfiguration class is in one of subpackages of SampleBootProjectApplication, defining packages at #ComponentScan limits packages scanned, so scanning never visits "com.sampleapp.security" package where SecurityConfiguration class extending WebSecurityConfigurerAdapter resides.
Removing package declarations at ComponentScan or adding "com.sampleapp.security" fixed it.
#ComponentScan({ "com.sampleapp.controller", "com.sampleapp.business" })
public class SampleBootProjectApplication extends SpringBootServletInitializer {
Well, I am currently programming on a project with Spring Security where I'd like to register a user in the database.
The proble is, if I vistit the endpoint where the registration should happen I get redirected to the default Login page of Spring Boot which should not happen.
This is my SecurityConfiguration.java
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder encodePasswd(){
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}
My RestController called UserController.java looks like this:
#RestController
#RequestMapping("/secure/rest")
public class UserController {
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#PostMapping("user/register")
public String addUser(#RequestBody User user){
String passwd = user.getPasswordUser();
String encryptPasswd = passwordEncoder.encode(passwd);
user.setPasswordUser(encryptPasswd);
userRepository.save(user);
return "addedUser";
}
}
Try this.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().disable();
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsInfoService userDetailsService;
#Autowired
private CustomLoginAuthenticationProvider authenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/api/user/authenticate/**")
.antMatchers("/api/user/**")
.antMatchers("/api/master/**")
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/* #EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}*/
}
This is my spring security configuration in the application.
It works fine most of the scenarios, but not able to track one issue.
Whenever I call a web service with a date format spring security returns 401 error.
http://localhost:9190/nazouki/api/master/create
with input
{"orgId":"11","birthDate":"2020-04-20T20:00:00.000Z"}
without date it works fine.
Controller code
#RequestMapping(value = "master/create", method = RequestMethod.POST)
public #ResponseBody ResultDecorator createMaster(#RequestBody TuMasterDto tuMasterDto){ handler.resolveResult(masterService.createMaster(tuMasterDto), OperationEnum.SAVE);
}
Exception has been thrown from the CustomAuthenticationEntryPoint in the spring security.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
We can't say for sure unless we also have a look at the handler.resolveResult() and the masterService.createMaster() methods.
My initial assumption would be that you have a GlobalMethodSecurity configuration on one or both of these methods (or a code path that is used by them), and since you ignore the security for the path web.ignoring().antMatchers("/api/master/**"), there is no SecurityContext created and therefore you get that response.
As to why it behaves differently when you add the date than without, I'm not so sure myself. Maybe you have 2 implementations? something like this
public void handleRequest(Dto dto) {
if(dto.getDate() != null) {
doSomethingWithTheDate(dto.getDate());
}
// handle normally
}
#PreAuthorize("hasRole('ROLE_USER')")
public void doSomethingWithTheDate(Date date) {
...
}
I'm trying to create an oauth2-enabled auth server which is able to authenticate users with two authentication providers: the former is in-memory (for default user-passwords) the latter is an external LDAP server (by now i'm using the example from gs-authenticating-ldap-complete).
I'm able to successfully retrieve an access token for any user, but i'm only able to use the refresh token for retrieving a new token for any user that is registered in the LDAP server. While everything is fine if I try to refresh an in-memory user's token, with the LDAP ones I get: 401 Unauthorized { "error": "unauthorized", "error_description": "ben" } where "ben" is the user id.
As far as I know (after some debugging) the exception occurs in DefaultTokenServices.java:150.
In the following I report the configuration classes I'm using.
#Configuration
#EnableWebSecurity
#Order(6)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
#Configuration
protected static class DefaultUsersAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and().withUser("guest")
.password("guest").roles("USER");
}
}
#Configuration
protected static class LDAPAuthConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups")
.userDetailsContextMapper(new MyLdapUserDetailsMapper()).contextSource()
.ldif("classpath:test-server.ldif");
}
}
protected static class MyLdapUserDetailsMapper extends LdapUserDetailsMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
final UserDetails originalUser = super.mapUserFromContext(ctx, username, authorities);
final Set<GrantedAuthority> newAuth = new HashSet<>(originalUser.getAuthorities());
newAuth.add(new SimpleGrantedAuthority("ROLE_EXTRA_ROLE"));
return new User(originalUser.getUsername(), originalUser.getPassword(), originalUser.isEnabled(),
originalUser.isAccountNonExpired(), originalUser.isCredentialsNonExpired(),
originalUser.isAccountNonLocked(), newAuth);
}
}
}
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends OAuth2AuthorizationServerConfiguration {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("acme").secret("acmesecret")
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.scopes("read", "write", "openid").autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/me").authenticated();
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore);
}
}
}
I'm using spring-boot 1.3.2.RELEASE. What am I missing?
I am trying to go over the following documentation: https://github.com/spring-projects/spring-security-oauth/blob/f25592e682303b0cf89e1d7555174bac18e174df/docs/oauth2.md#mapping-user-roles-to-scopes
In the documentation, it says in order to map user roles to scopes, along with setting the checkUserScopes=true in the DefaultOAuth2RequestFactory, we need to add the TokenEndpointAuthenticationFilter filter after the HTTP BasicAuthenticationFilter. I was wondering how that could be done.
Here is what my AuthorizationServer looks like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends
AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private OAuth2RequestFactory requestFactory;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
endpoints.requestFactory(requestFactory);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
}
#Bean
public ClientDetailsService clientDetailsService() {
Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
Collection<String> scope = new HashSet<String>();
scope.add("user");
scope.add("admin");
Collection<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client");
clientDetails.setClientSecret("secret");
clientDetails.setScope(scope);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
clientDetailsStore.put("client", clientDetails);
InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();
clientDetailsService.setClientDetailsStore(clientDetailsStore);
return clientDetailsService;
}
#Bean
public OAuth2RequestFactory requestFactory() {
DefaultOAuth2RequestFactory requestFactory =
new DefaultOAuth2RequestFactory(clientDetailsService());
requestFactory.setCheckUserScopes(true);
return requestFactory;
}
}
Also, it would be fantastic to provide a sample CURL on how we can test the grant-type password.
Appreciate any help!
Instead of using #EnableAuthorizationServer you should be able to extend AuthorizationServerSecurityConfiguration and include that in your Spring configuration. E.g.
#Configuration
public class OAuth2Config extends AuthorizationServerSecurityConfiguration {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.addFilterAfter(myFilter(), BasicAuthenticationFilter.class);
}
}
You can add also add additional filters via the AuthorizationServerSecurityConfigurer, though they come before Basic auth, not after.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.addTokenEndpointAuthenticationFilter(myFilter());
security.checkTokenAccess("isAuthenticated()");
}
Adds a new custom authentication filter for the TokenEndpoint. Filters will be set upstream of the default BasicAuthenticationFilter.