Spring Security Active Directory - spring-security

I am trying to do LDAP authentication by Spring Security.
My code...
auth.ldapAuthentication()
.userSearchFilter("(uid={0})").userSearchBase("ou=TTU")
.groupSearchFilter("uniqueMember={0}").groupSearchBase("ou=TTU")
.contextSource(contextSource())
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
But always return 401 "Bad credentials"
What can be the kind of mistake?
Perhaps someone has an example with Java config.

it's work ... maybe anybody willbe helpfull.
auth.authenticationProvider(ldapAuthenticationProvider());
auth.eraseCredentials(true);
#Bean
public DefaultSpringSecurityContextSource contextSource(){
DefaultSpringSecurityContextSource contextSource =
new DefaultSpringSecurityContextSource(Arrays.asList("ldap://url:389/"),"dc=ttu,dc=ru");
contextSource.setUserDn(userDn);
contextSource.setPassword(passwordForLDAP);
contextSource.setReferral("follow");
return contextSource;
}
#Bean
public LdapAuthenticationProvider ldapAuthenticationProvider(){
return new LdapAuthenticationProvider(ldapAuthenticator(),ldapAuthoritiesPopulator());
}
#Bean
public LdapAuthenticator ldapAuthenticator(){
BindAuthenticator authenticator = new BindAuthenticator(contextSource());
authenticator.setUserSearch(userSearch());
return authenticator;
}
#Bean
public DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator(){
DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator =
new DefaultLdapAuthoritiesPopulator(contextSource(),"ou=TTU");
ldapAuthoritiesPopulator.setSearchSubtree(true);
ldapAuthoritiesPopulator.setIgnorePartialResultException(true);
//ldapAuthoritiesPopulator.setGroupSearchFilter("member={0}");
ldapAuthoritiesPopulator.setRolePrefix("ROLE_");
ldapAuthoritiesPopulator.setConvertToUpperCase(true);
return ldapAuthoritiesPopulator;
}
#Bean
public FilterBasedLdapUserSearch userSearch(){
FilterBasedLdapUserSearch filterBasedLdapUserSearch =
new FilterBasedLdapUserSearch("","(sAMAccountName={0})",contextSource());
filterBasedLdapUserSearch.setSearchSubtree(true);
return filterBasedLdapUserSearch;
}

Related

Authentification manager + database

I am trying to set up an authentication system on my spring boot application using spring boot security but I am having trouble getting it to work with my database.
If I try to log in with the credentials of a user created in my InMemoryUserDetailsManager, my AuthenticationManager works completely (it throws an error if the credentials are wrong).
However, if I try with the credentials of a user in my database, it doesn't work because it seems to not be logged in with my AuthenticationManager.
I'm new to spring boot and spring security, sorry if my problem is a bit stupid.
Here is my code that might help you understand my problem:
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
return new InMemoryUserDetailsManager(
User.withUsername("user1").password(passwordEncoder.encode("1234")).authorities("USER").build(),
User.withUsername("user2").password(passwordEncoder.encode("1234")).authorities("USER").build(),
User.withUsername("admin").password(passwordEncoder.encode("1234")).authorities("USER","ADMIN").build()
);
}
#Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService){
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(passwordEncoder);
authProvider.setUserDetailsService(userDetailsService);
return new ProviderManager(authProvider);
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(csrf ->csrf.disable())
.authorizeHttpRequests(auth->auth.requestMatchers("/auth/**").permitAll())
.authorizeHttpRequests(auth ->
auth.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.httpBasic(Customizer.withDefaults())
.build();
}
#PostMapping("/login")
public Map<String,String> Login(#RequestBody User user){
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())
);
My database is correctly configured and contains a user
For someone that could be in the same situation as me, u must add this in your code :
#Bean
public UserDetailsService userDetailsService(){
return email->userRepository.findByEmail(email).orElseThrow(()->new UsernameNotFoundException("User not found"));
}
Moreover, u must change your authenticationManager like this :
#Bean
public AuthenticationManager authenticationManager(){
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(passwordEncoder);
authProvider.setUserDetailsService(userDetailsService());
return new ProviderManager(authProvider);
}

Spring Security 5 - how to use symmetric signingKey?

I have micro-services running using old Spring Authorization Server and Resource Server (org.springframework.cloud:spring-cloud-starter-oauth2 deprecated now).
For new micro-service I am creating, I need to use newer version of Spring Security 5 implementation of Resource Server. (org.springframework.boot:spring-boot-starter-oauth2-resource-server)
Below is code from my authorization server.
#Configuration
public class AuthorizationServerConfig implements AuthorizationServerConfigurer {
private final PasswordEncoder passwordEncoder;
private final DataSource dataSource;
private final AuthenticationManager authenticationManager;
private final UserDetailsService userDetailsService;
private final CustomAccessTokenConverter customAccessTokenConverter;
#Value("${signing-key:123}")
private String signingKey;
public AuthorizationServerConfig(
PasswordEncoder passwordEncoder,
DataSource dataSource,
AuthenticationManager authenticationManager,
UserDetailsService userDetailsService,
CustomAccessTokenConverter customAccessTokenConverter) {
this.passwordEncoder = passwordEncoder;
this.dataSource = dataSource;
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.customAccessTokenConverter = customAccessTokenConverter;
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setAccessTokenConverter(customAccessTokenConverter);
jwtAccessTokenConverter.setSigningKey(signingKey);
return jwtAccessTokenConverter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
#Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore());
endpoints.approvalStore(approvalStore());
endpoints.authorizationCodeServices(authorizationCodeServices());
endpoints.authenticationManager(authenticationManager);
endpoints.tokenEnhancer(tokenEnhancerChain);
endpoints.userDetailsService(userDetailsService);
}
}
How do I use the symmetric signing key to decrypt JWT in Spring Security 5?
From migration guide, https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide I have option to use JWT+JWK or JWT+Key but not sure what to use as my signing key is just a string.
I had faced exactly the same problem mentioned above. I have the old oaut2 Authorization Service and migrate the old Resource Service to Spring Security 5 standards.
So, it's my proposition of this problem. I chose JWT + Key scenario from OAuth2 Migration Guide.
In application.yml file I added the same singleKey and additional configuration for debugging SecurityFilterChain to be able to control authorization flow according to docs:
spring:
security:
oauth2:
resourceserver:
jwt:
key-value: zSigFffuyF5CjmLAxubSTVBt5OnfbnGPkFpvNpg2n/.....
logging:
level:
org:
springframework:
security: DEBUG
In my configuration class, I injected singleKey by #Value annotation:
#Value("${spring.security.oauth2.resourceserver.jwt.key-value}")
private String key;
then I exposed default configuration on SecurityFIlterChain:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
.oauth2ResourceServer((oauth2) -> oauth2.jwt((jwt) -> jwt.decoder(jwtDecoder())));
return http.build();
}
and for customized JTWDecoder bean adding converting of my singleKey to SecretKey object:
#Bean
public JwtDecoder jwtDecoder() {
byte[] secretByte = key.getBytes();
SecretKey secretKey = new SecretKeySpec(secretByte, 0, secretByte.length, "RSA");
return NimbusJwtDecoder.withSecretKey(secretKey).build();
}
Logs withs configured debbugig:
o.s.security.web.FilterChainProxy.doFilterInternal - Securing GET /example_endpoint
o.s.s.w.c.SecurityContextPersistenceFilter.doFilter - Set SecurityContextHolder to empty SecurityContext
o.s.s.o.s.r.a.JwtAuthenticationProvider.authenticate - Authenticated token
o.s.s.o.s.r.w.BearerTokenAuthenticationFilter.doFilterInternal - Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt#29cc3dff, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=172.16.57.7, SessionId=null], Granted Authorities=[SCOPE_BOU]]
o.s.security.web.FilterChainProxy.doFilter - Secured GET /example_endpoint
Like in docs: https://docs.spring.io/spring-security/reference/5.7.2/servlet/oauth2/resource-server/jwt.html#oauth2resourceserver-jwt-architecture

Does anyone have a UserDetails Service Example for Spring Cloud OAuth2 and Active Directory?

I have a Spring Cloud (Edgeware.SR3) OAuth2 Authorization server configured with Custom JWT tokens. I'm getting an IllegalStateException, UserDetailsService is required error when I hit the token_refresh endpoint.
Does anyone have an example of doing a UserDetails Service for Active Directory for this scenario? I presume the call for refreshing the token is actually checking against AD if the user is still valid such as not disabled since last successful login.
Not shown is I'm also doing integrations to AWS Cognito in the custom token enhancer which is also all working. Just the refresh token is what remains.
#Configuration
public class ServiceConfig extends GlobalAuthenticationConfigurerAdapter {
#Value("${ldap.domain}")
private String DOMAIN;
#Value("${ldap.url}")
private String URL;
#Override
public void init(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
--------
#Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfiguration(AuthenticationManager authenticationManager){
super();
this.authenticationManager = authenticationManager;
}
#Value("${signing.key}")
private String signingKey;
#Bean
public JwtAccessTokenConverter accessTokenConverter(){
final JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey(signingKey);
return accessTokenConverter;
}
#Bean
public TokenStore tokenStore(){
return new JwtTokenStore(accessTokenConverter());
}
#Bean
#Primary
public DefaultTokenServices tokenServices(){
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
return tokenServices;
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("${client.id}")
.secret("${client.secret}")
.authorizedGrantTypes("password","refresh_token","authorization_code","client_credentials")
.refreshTokenValiditySeconds(3600 *24)
.scopes("xx","xx")
.autoApprove("xxxx")
.accessTokenValiditySeconds(3600);
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints){
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(),accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
.accessTokenConverter(accessTokenConverter());
}
#Override//oauth/check_token?token={access_token}
public void configure(final AuthorizationServerSecurityConfigurer security)throws Exception {
security.checkTokenAccess("permitAll()");
super.configure(security);
}
#Bean
public TokenEnhancer tokenEnhancer(){
return new CustomTokenEnhancer();
}
}

spring-security redirect 404 error

I'm using spring boot security as ACL for my restful services.
The security adapter as below
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableRedisHttpSession
#Order(2)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService userDetailsService;
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().userDetailsService(userDetailsService);
}
}
The snap of userdetailservice
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Yuangong yuangong = yuangongService.getYuangongByNo(username).getData();
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<SimpleGrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ALL"));
return new User(yuangong.getNo(), yuangong.getPassword(), grantedAuthorities);
}
The endpoint annotated by #RestController, and the method in endpoint like
#RestController
#RequestMapping(path = "/bumen")
public class BumenEndpoint {
// #PermitAll
#PreAuthorize("hasRole('ROLE_ALL')")
#RequestMapping(path = "/getBumenTreeList", method = RequestMethod.GET )
public HttpResult<List<Map<String, Object>>> getBumenTreeData(Principal principal) {
System.out.println(principal.getName());
return new HttpResult(bumenService.getBumenTreeList());
}
If I use #permitAll, it worked find and return the right JSON response. If using #PreAuthorize("hasRole('ROLE_ALL')"), it can pass the auth and can debug into this method, but the response will be redirected to "/bumen/bumen/getBumenTreeList" (double '/bumen') with 404 error.
if I don't implements the BumenEndpoint, there will not being redirected and return the right response.
I'm not sure which part cause the redirecting.
The issue was caused by the annotation. I have fixed it as per this Spring-MVC Problem using #Controller on controller implementing an interface

How to secure Apache Camel rest endpoint with Spring Security and OAuth2

I'm working on Spring Boot application with configured SSO/OAuth2 security.
Authentication works fine for my rest controllers and now I need to secure my Apache Camel route with a rest endpoint.
As I understand there are several ways how to do it:
By adding auth processor to my route
By adding policy (SpringSecurityAuthorizationPolicy) to my route
By handlers option to jetty endpoint
I'm trying to do it by adding new auth processor to my rest endpoint but I stuck on this exception:
org.springframework.security.oauth2.common.exceptions.OAuth2Exception:
No AuthenticationProvider found for
org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
During debugging I see that org.springframework.security.authentication.ProviderManager.getProviders() contains only one provider AnonymousAuthenticationProvider so probably I have to register appropriate provider...
Can someone help me to find the right way to solve this problem please?
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${oauth2.token.endpoint}")
private String tokenEndpoint;
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId("clientId");
tokenServices.setClientSecret("clientSecret");
tokenServices.setCheckTokenEndpointUrl(tokenEndpoint);
return tokenServices;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
}
#Configuration
public class EmbeddedServerRoute {
#Bean
public RoutesBuilder embeddedServer() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
restConfiguration().component("jetty").port("8081").bindingMode(RestBindingMode.json);
}
};
}
}
#Component
public class RestTestRoute extends RouteBuilder {
#Autowired
private AuthProcessor authProcessor;
#Override
public void configure() throws Exception {
from("rest:get:/test").process(authProcessor).to("mock:end").end();
}
}
#Component
public class AuthProcessor implements Processor {
#Autowired
private AuthenticationManager authenticationManager;
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new OAuth2AuthenticationDetailsSource();
#Override
public void process(Exchange exchange) throws Exception {
HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
Subject subject = new Subject();
Authentication auth = getAuth(request);
subject.getPrincipals().add(auth);
exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);
}
private Authentication getAuth(HttpServletRequest request) throws OAuth2Exception {
Authentication authentication = null;
try {
authentication = tokenExtractor.extract(request);
if (authentication != null) {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
return authenticationManager.authenticate(authentication);
}
} catch (Exception e) {
throw new OAuth2Exception(e.getMessage());
}
throw new OAuth2Exception("Not Authorized to view resource");
}
}
As a final solution I decided to use Spring Boot embedded servlet container instead of Apache Camel rest component. So it could be easily secured by Spring Security. This could be done by creating additional beans:
#Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean(serverServlet, "/camel/*");
Map<String, String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
#Bean
public Component restletComponent() {
return new Component();
}
#Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}

Resources