I have a working spring security that works againts a local db. Now I'd like to move the authentication part by calling a webservice as follow
RestTemplate rt = new RestTemplate();
boolean valid = rt.getForObject(HostConfig.HOST+"/authenticate?username=hq&password=a123456",Boolean.class);
The above service returns true or false.
I'd like to know on which class I should override the existing authentication with the above. Any ideas?
org.springframework.security.core.userdetails.UserDetailsService is the thing that you are looking for. You should implement the method loadUserByUsername(), and check the password etc.
HTH
Related
I have a Spring Boot application, that is using Spring Security with OAuth 2.0. Currently, it is operating against an Authentication Server based on Spring Example code. However, running our own Auth Server has always been a short-term target to facilitate development, not a long-term goal. We have been using the authorization_code grant type and would like to continue using that, irrespective of the Auth Server implementation.
I am attempting to make changes to use OAuth 2.0 Endpoints in Azure Active Directory, to behave as our Authentication Server. So far, I have a successful call to the /authorize endpoint. But the call to get the /token fails with an invalid request error. I can see the requests going out.
It appears that parameters that Azure states as mandatory are not being populated in the POST request. Looking at the Azure doco, it expects the client_id to be defined in the body of the message posted to the endpoint, and that is not added, by default, by Spring.
Can anyone point me in the right direction for how I can add fields to the Form Map that is used when constructing the Access Token request? I can see where the AccessTokenRequest object is being setup in OAuth2ClientConfiguration....
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
protected AccessTokenRequest accessTokenRequest(#Value("#{request.parameterMap}")
Map<String, String[]> parameters, #Value("#{request.getAttribute('currentUri')}")
String currentUri) {
DefaultAccessTokenRequest request = new DefaultAccessTokenRequest(parameters);
request.setCurrentUri(currentUri);
return request;
}
Should I be trying to define the map in a request.parameterMap spring property? If so, I'm not too sure how that works.
Or should I be using one of the interfaces defined in the AuthorizationServerConfigurerAdapter class?
I have the information to include when sending the AccessTokenRequest, I just don't know the best way to configure Spring to include it? Thanks for any help.
Actually, I found this out. I needed to change the client authentication scheme. Simply adding the following to my application properties added the client_id to the form....
security.oauth2.client.clientAuthenticationScheme=form
If you're using yaml, then yaml-ize it. Thank you Spring!
I'm using #WithMockUser to test a controller method secured with basic auth in a simple spring boot web service. My annotation looks like:
#WithMockUser(username = 'admin', password = 'mypassword', roles = ['ADMIN'])
It seems however that the username and password field are ignored. The test passes even if I specify an incorrect username or password. The roles field however does appear to be used and the test fails with an invalid role is provided.
Perhaps I'm misunderstanding the role of this annotation, but should I be able to use it to verify behavior of incorrect credentials being supplied to a secured method?
Thanks!
--john
If you are using Spring MVC Test (i.e., MockMvc), you would do something like the following:
mvc.perform(get("/").with(httpBasic("user","password"))) // ...
This is documented in the Testing HTTP Basic Authentication section of the Spring Security reference manual.
If you have configured your test to launch Spring Boot with an embedded Servlet container, you can use Spring Boot's TestRestTemplate which supports "Basic HTTP authentication (with a username and password)".
This has happened to me as well. I think #WithMockUser is enough to test secured services. user/password/roles is not required. I validated this scenario in my case.
Background
I have a multitenant application that uses multiple databases per-tenant. I'm using CodeFirstMembership, so I have full controll over the SimpleMembership implementation. Both my User/Role entities are in the same DbContext as the rest of my application.
The Problem
In order to facilitate multi-tenance, I have a custom route that looks exactly the same as what the default vanilla MVC route looks like, with the exception that I grab the subdomain, check it against the tenants that have an account, and grab their specific connection string. I have an extension method on the RouteData called .GetSubdomain() that will return the subdomain used, so I can really do the check and get the connection string wherever, if that helps you with your answer.
I need my membership provider to be able to access the subdomain check information in order to point to the correct database for the [Authorize] method to work correctly.
What I've tried
Initializing the membership provider in the InitializeSimpleMembershipAttribute
This didn't work because you can't pass in dynamic parameters into attributes (like RouteData.GetSubdomain())
Initializing the membership in the constructor.
While you can call RouteData methods in the constructor and have the app build/run, RouteData has not been populated at the point of the constructor in a controller, so this method didn't work either.
I didn't try this, but adding the check at the start of each controller method is likely not to work since the authorization has already ran.
So...
Out of what I've tried, it seems like I need to hook into the point between where RouteData is populated and the actual Authorization. Is there a point I can do this effectively?
Thanks!
WOW. In a total oversight, I failed to see the ActionExecutingContext that was being passed into the InitializeSimpleMembership attribute. The context being passed in actually contains RouteData, and the subdomain information needed to properly initialize the membership!
i have some code that tries impersonate the callers windows security settings and then connect to another WCF service on a different machine
WindowsIdentity callerWindowsIdentity = ServiceSecurityContext.Current.WindowsIdentity;
using (callerWindowsIdentity.Impersonate())
{
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
EndpointAddress endpoint = new EndpointAddress(new Uri("net.tcp://serverName:9990/TestService1"));
ChannelFactory<WCFTest.ConsoleHost.IService1> channel = new ChannelFactory<WCFTest.ConsoleHost.IService1>(binding, endpoint);
WCFTest.ConsoleHost.IService1 service = channel.CreateChannel();
return service.PrintMessage(msg);
}
But I get the error:
"the caller was not authenticated by the service"
System.ServiceModel .... The request for security token could not be satisfied because authentication failed ...
The credentials I am trying to impersonate are valide windows credential for the box the service is on.
Any ideas why?
In order to support your scenario, you need to have an understanding of how Protocol Transition and Constrained Delegation work. You will need to configure both Active Directory and your WCF service endpoint(s) to support this. Note the use of the Service Principal Name (SPN). Take a look at the following link and see if they help you. The article has a sample to demonstrate the complete end-to-end configuration required to make this work.
How To: Impersonate the Original Caller in WCF Calling from a Web Application
Agree with marc_s this is the double-hop problem.
You need to get the windows authentication all the way through, therefore:
The request must be made in the context of a windows users
IIS must be configured to use windows authentication
Web.config must be set up for windows authentication with impersonate = true
The user that your application pool is running as, must be allowed to impersonate a user. This is the usual place where the double-hop problem occurs.
There is a right called "Impersonate a client after authentication"
http://blogs.technet.com/askperf/archive/2007/10/16/wmi-troubleshooting-impersonation-rights.aspx
Impersonation from you service to the next is a tricky issue, known as "double-hop" issue.
I don't have a final answer for that (I typically avoid it by using an explicit service account for the service that needs to call another service).
BUT: you should definitely check out the WCF Security Guidance on CodePlex and search for "Impersonation" - there are quite a few articles there that explain all the ins and outs of impersonating an original caller and why it's tricky.
Marc
If you are sure you have the credentials right on both hops, the next thing that could be causing the issue is the lack of the EndpointDnsIdentity being set on the endpoint.
DnsEndpointIdentity identity = new DnsEndpointIdentity("localhost"); // localhost is default. Change if your service uses a different value in the service's config.
Uri uri = new Uri("net.tcp://serverName:9990/TestService1");
endpoint = new EndpointAddress(uri, identity, new AddressHeaderCollection());
I have a simple web service whereby the security is handled via forms based authentication.
WCFTestService.ServiceClient myService = new
WCFTestService.ServiceClient();
myService.ClientCredentials.UserName.UserName = "user";
myService.ClientCredentials.UserName.Password = "secret";
lblResult.Text = myService.GetData(1231);
myService.Close();
I'm accessing this via a web app. So I want to do the above once but for security/performance not have to do it again. I was thinking something like the the below but as I'm using FormsAuthentication this wont work...
//Obtain the authenticated user's Identity and impersonate the original caller
using (((WindowsIdentity)HttpContext.Current.User.Identity).Impersonate())
{
WCFTestService.ServiceClient myService2 = new WCFTestService.ServiceClient();
lblResult.Text = "From Logged On Credentials"+myService2.GetData(1231);
myService2.Close();
}
What you're trying to do is establish a "secure session" between your client and your service. This is a concept that will only work with the wsHttpBinding - so if you're not using that particular binding, it won't work.
To establish a secure session, you need to set a number of specific config properties in the client and server's config files - you can certainly find those settings by reading the docs (look for "establishSecurityContext") or check out Michele Leroux Bustumante's excellent WCF screencast on security fundamentals on MSDN.
But really: I wouldn't recommend trying to use secure session by all means. Under normal circumstances, using per-call services is the preferred option, and the overhead for re-authenticating with each service call is really negligable.
Marc