I have created an Authorization service as follows
#SpringBootApplication
#EnableAuthorizationServer
public class AuthorizationApplication {
...
}
With this application.properties.
server.port=9000
security.oauth2.client.client-id=monederobingo
security.oauth2.client.client-secret=monederobingosecret
security.oauth2.client.authorized-grant-types=authorization_code,refresh_token,password,client_credentials
security.oauth2.client.scope=company,client
Then, in a separate spring boot project I have created a Resource Server.
#SpringBootApplication
#EnableResourceServer
public class App {
...
}
With this application.properties.
server.port=9090
spring.application.name=app
security.oauth2.resource.user-info-uri=http://localhost:9000/user
Now, everything works fine if I send a request like this localhost:9090/api with the appropriate token that was retrieved by Authorization Service.
However, I don't want to send this token when sending requests to localhost:9090/login.
For this I have created this class in my Resource server spring boot app.
#Configuration
public class SpringConfig extends WebSecurityConfigurerAdapter {
#Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login")
.permitAll()
.antMatchers("/api/**")
.authenticated();
}
}
And now I don't need to send any token to send a request to /login.
However, I'm now geting the following message when sending request to /api with a valid token.
{
"timestamp": 1496027102659,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/v1/points_configuration/314"
}
How can configure security for only a few URL patterns in Spring Security OAuth2?
Kindly follow this for more info regarding Spring OAuth security:Secure Spring REST Api with OAuth
In order to implement OAuth Security in Spring boot, you have to create Authorization & Resource server by extending them from AuthorizationServerConfigurerAdapter and ResourceServerConfigurerAdapter respectively.
Authorization Server
#Configuration
#EnableAuthorizationServer
public class AuthorizationApplication extends AuthorizationServerConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.userDetailsService(userDetailsService)
.authenticationManager(this.authenticationManager).tokenStore(tokenStore()).approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(mongoClientDetailsService);
/*inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities("ROLE_CLIENT")
.authorizedGrantTypes("password", "refresh_token","client_credentials")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 18000));*/
}
//Do others stuff
}
Resource Server
All the Url that you want to protect using OAuth should be mentioned in this server configuration. It enables a Spring Security filter that authenticates requests using an incoming OAuth2 token. While mostly WebSecurityConfigurerAdapter extended class is used for basic security configuration like adding filters, allowing un-secure url or implementing session policies etc.
#Configuration
#EnableResourceServer
public class App extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**").and().authorizeRequests()
.antMatchers("/api/**").authenticated();
}
//Do others stuff
}
Related
We have used the spring authentication server for providing my other spring boot application(Resource server). There are some endpoints that do not need to get authenticated for a specific host.
I have used the bellow code example but it works only without the OAuth server.
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/url/**").permitAll().
anyRequest().authenticated();
}
Have you tried
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(2)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/media/allVideos")
.permitAll()
.anyRequest()
.authenticated();
}
}
https://spring.io/guides/tutorials/spring-boot-oauth2/
I want to implement central authentication system with spring security and oauth2 sso. In other words, I have a spring boot application that is responsible for authorization and one simple client. My client has rest API. First I get token from the authorization server, then send a request to client API with an authorization header contains bearer token from above request. But this request always gets me server login page.
Here is the implementation of the server and the client:
Server
AuthorizationServerConfig.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret("{noop}secret")
.authorizedGrantTypes("password")
.scopes("user_info")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
ApplicationConfig:
#SpringBootApplication
#EnableResourceServer
public class ApplicationConfig extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
SecurityConfig:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//this is just example
auth.inMemoryAuthentication().withUser("user").password("{noop}1234").roles("user");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/token")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
application.yml:
server:
port: 8900
servlet:
context-path: /auth
Client:
ApplicationConfig:
#SpringBootApplication
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
SecurityConfig:
#Configuration
#EnableOAuth2Sso
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
TestController:
#RestController
public class HomeController {
#GetMapping("/")
public String index() {
return "home";
}
#RequestMapping("/admin")
public String admin() {
return "admin";
}
}
application.yml:
server:
port: 9000
servlet:
context-path: /client1
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8900/auth/oauth/token
userAuthorizationUri: http://localhost:8900/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8900/auth/user/me
First, I send client_id and secret code along side with username, password and grant_type to localhost:8900/auth/oauth/token and get a result like this:
{
"access_token": "603b505f-e701-43d0-b8b8-976a2178f7ea",
"token_type": "bearer",
"expires_in": 43199,
"scope": "user_info"
}
Now, I pickup above token and send a request to localhost:9000/client1/admin
with header contains above token. But it seems the client application ignores the header and shows server login page as result. How can I fix this problem?
#EnableOAuth2Sso is an annotation for using OAuth 2.0 as an end-user authentication mechanism (e.g. "A Login with Google" button). This annotation is wiring your app to redirect to a login page on your authorization server where you would log in and then get redirected back to your app.
If this is your intent, then you'll need to update your Authorization Server to support the authorization_code grant flow instead of the password grant flow.
However, if your client is strictly a REST API, then you are more likely to need to wire the client using #EnableResourceServer instead of #EnableOAuth2Sso. A Resource Server is what takes a token as authorization, via the Authorization HTTP header.
I'm creating a spring boot 2.0 application and trying to enable oauth2 security. I have Auth server and Resource server in the same application as of now. My client and user details as well as token generated are persisted in databases (mysql) and database schema is the same as provided by spring documentation. When I hit the '/oauth/token/' endpoint providing clientId and clientSecret in header and user's credentials in body using Postman, I'm getting access token successfully.
{
"access_token": "bef2d974-2e7d-4bf0-849d-d80e8021dc50",
"token_type": "bearer",
"refresh_token": "32ed6252-e7ee-442c-b6f9-d83b0511fcff",
"expires_in": 6345,
"scope": "read write trust"
}
But when I try to hit my rest api using this access token, I'm getting 401 Unauthorized error:
{
"timestamp": "2018-08-13T11:17:19.813+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/myapp/api/unsecure"
}
The rest APIs I'm hitting are as follows:
http://localhost:8080/myapp/api/unsecure
http://localhost:8080/myapp/api/secure
myapp is the context path of my application.
For 'secure' api, I have provided access token in request header as described in Spring documentation:
Authorization: Bearer bef2d974-2e7d-4bf0-849d-d80e8021dc50
Whereas for unsecure api, I have tried with and without Authentication header. In all cases I'm getting same error for both apis.
Also when I try to print currently authenticated user, its getting printed as anonymousUser.
What I want are as follows:
1) I want my secure api to be accessible only when access token is provided in request header.
2) I want my unsecure api to be accessible by unauthorised user.
3) I should get currently authenticated user using SecurityContextHolder when accessing secure url.
My WebSecurityConfigurerAdapter is as follows:
#Configuration
#EnableWebSecurity(debug=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
UserDetailsService userDetailsService;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public PasswordEncoder userPasswordEncoder() {
return new BCryptPasswordEncoder(8);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(userPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws
Exception {
return super.authenticationManagerBean();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore
tokenStore){
TokenStoreUserApprovalHandler handler = new
TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new
DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws
Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/index.html", "/**.js", "/**.css", "/").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
Here using antMatchers I have permitted static pages of Angular 6 application, as I'm planning to use those in my real app. And no, the following line does not work to allow static pages of angular application:
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
My AuthorizationServerConfigurerAdapter is as follows:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
UserDetailsService userDetailsService;
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(passwordEncoder);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
My ResourceServerConfigurerAdapter is as follows:
#Configuration
#EnableResourceServer
public abstract class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource-server-rest-api";
#Autowired
TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(RESOURCE_ID)
.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.anonymous().disable()
.requestMatchers()
.antMatchers("/api/**").and()
.authorizeRequests()
.antMatchers("/api/secure").authenticated()
.antMatchers("/api/unsecure").permitAll();
}
}
But when I enable anonymous access in SecurityConfig and declare my unsecure url as permitAll, then I'm able to access that url.
.antMatchers("/api/unsecure", "/index.html", "/**.js", "/**.css", "/").permitAll()
My Controller class is as follows:
#RestController
#RequestMapping("/api")
public class DemoController {
#GetMapping("/secure")
public void sayHelloFriend() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Current User: "+authentication.getName());
System.out.println("Hello Friend");
}
#GetMapping("/unsecure")
public void sayHelloStranger() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Current User: "+authentication.getName());
System.out.println("Hello Stranger");
}
}
Let me know if any more information is needed. Any help will be appreciated. But please keep in mind that its Spring Boot 2.0 not 1.5 as both have some critical differences as per my findings.
Try to added
#Order(SecurityProperties.BASIC_AUTH_ORDER)
for the securityConfig? so the chain will check your Resource server's config first.
And not sure if that your type error, remove the abstract from the resource server.
I know almost nothing about LDAP and even less about spring security but I am trying to configure a spring boot app to authenticate against an ldap instance and am stuck.
I was given the ldap server name at adldap.company.com and base dn of dc=ad,dc=company,dc=com
I have some python code that does a simple bind and works.
LDAP_USERNAME = 'username#ad.company.com'
LDAP_PASSWORD = 'password'
base_dn = 'dc=ad,dc=company,dc=com' # not used for bind I guess, only search
try:
ldap_client = ldap.initialize('ldap://adldap.company.com')
ldap_client.set_option(ldap.OPT_REFERRALS,0)
ldap_client.simple_bind_s(LDAP_USERNAME, LDAP_PASSWORD)
except ldap.INVALID_CREDENTIALS as e:
ldap_client.unbind()
return 'Wrong username and password: %s' % e
except ldap.SERVER_DOWN:
return 'AD server not available'
If I run this code, it seems to successfully bind as "username#ad.company.com" with password "password".
I also have a WebSecurityConfig class that I think should be handling auth:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secure")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0}")
.contextSource()
.url("ldap://adldap.company.com");
//.url("ldap://adldap.company.com/dc=ad,dc=company,dc=com");
}
}
When I go to /secure in the app, I get a basic auth pop up but then anything I try entering gets me a 401 Unauthorized. I have tried "username#ad.company.com", without the domain, putting that stuff in the userDnPatterns like {0}#adldap.company.com and a bunch of other things. I have tried using different URLs with the base dn in it or not. Nothing seems to work. What am I missing?
Also, is this the right way to auth users? I've read about both bind authentication and something about binding and searching but the server doesn't allow anonyous binds so I guess I would need some kind of "app user" that could bind and do the searches, right? Is that "better"?
Active Directory has its own non-standard syntax for user authentication, different from the usual LDAP DN binding.
Spring Security provides a specialized AuthenticationProvider for Active Directory.
Try this :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secure")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("adldap.company.com", "ldap://adldap.company.com");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
Long story short, the problem is that Microsoft Active Directory LDAP is not "Vanilla" LDAP and thus you need to connect to it differently.
The working solution is here: https://medium.com/#dmarko484/spring-boot-active-directory-authentication-5ea04969f220
I have a system using Spring Boot, Angular 2, Spring OAuth 2 where I have implemented security using #EnableWebSecurity and implemented oauth using #EnableResourceServer and #EnableAuthorizationServer in the same application.
Following are the implemented classes:
SecurityConfig.java
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("pass").roles("USER").and()
.withUser("username").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/private/**").hasRole("USER")
.antMatchers("/public/**").permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthorizationServerConfig.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT","USER")
.scopes("read", "write", "trust")
.secret("secret")
.accessTokenValiditySeconds(1200).
refreshTokenValiditySeconds(6000);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("hasAuthority('USER')");
}
}
ResourceServerConfig.java
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/public/**").permitAll();
http.authorizeRequests().antMatchers("/private/**").hasRole("USER");
}
}
All the url following the /public are accessible by any users; it's correct. The urls following /private/ are secured by both ResourceServerConfig and SecurityConfig, therefore it's not accessible to anonymous users.
When I request access_token from authorization server using grant_type=password, I get the access_token which I use to access the secured resources by appending the access_token as the parameter. But still the resources are not available and I get the response as below:
localhost:8080/private/user/test/?access_token=92f9d86f-83c4-4896-a203-e21976d4cfa2
{
"timestamp": 1495961323209,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/private/user/test/"
}
But when I remove the antMatchers from SecurityConfig.configure(HttpSecurity), the resources are no longer secured even if the ResourceServerConfig.configure(HttpSecurity) is securing the patters.
My questions:
Is there anything I need to perform in ResourceServerConfig so as to grant access to authorized users from the resource server?
What is difference between #EnableResourceServer and #EnableWebSecurity? Do I need to implement both in this application? (I couldn't find any good answers for this questions)
Your private resource is well secured, but the access_token obtained is not passed the correct way to the server.
You have to pass it as header of the request with
Authorization: Bearer 92f9d86f-83c4-4896-a203-e21976d4cfa2
or as curl command:
curl -H "Authorization: Bearer 92f9d86f-83c4-4896-a203-e21976d4cfa2"