Spring Rest service security with OAUTH – XML
Lets discuss Spring Rest service security with OAuth using XML configuration
We have learned about securing Rest services and consuming secured Rest services using Spring Security in spring security rest service article.
In this article, we will learn about the same using OAuth instead of Spring security.
If you are completely new to OAuth then I would strongly recommend reading OAUTH overview article before going through this article.
Lets implement the same step by step
Create a new Maven Web project in eclipse (Refer Spring MVC Hello World project for the same)
Project structure
Step 1
Update pom.xml with below dependencies
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>SpringRestServiceSecurityOauth</groupId>
- <artifactId>SpringRestServiceSecurityOauth</artifactId>
- <packaging>war</packaging>
- <version>0.0.1-SNAPSHOT</version>
- <name>SpringRestServiceSecurityOauth Maven Webapp</name>
- <url>http://maven.apache.org</url>
- <properties>
- <org.springframework.version>4.2.0.RELEASE</org.springframework.version>
- <spring-security.version>3.2.7.RELEASE</spring-security.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <!-- Jackson JSON -->
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.8.5</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-core</artifactId>
- <version>${spring-security.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-web</artifactId>
- <version>${spring-security.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-config</artifactId>
- <version>${spring-security.version}</version>
- </dependency>
- <!-- Spring OAUTH dependency -->
- <dependency>
- <groupId>org.springframework.security.oauth</groupId>
- <artifactId>spring-security-oauth2</artifactId>
- <version>2.0.12.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.6</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-jdbc</artifactId>
- <version>4.3.5.RELEASE</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>SpringRestServiceSecurityOauth</finalName>
- </build>
- </project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>SpringRestServiceSecurityOauth</groupId> <artifactId>SpringRestServiceSecurityOauth</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>SpringRestServiceSecurityOauth Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <org.springframework.version>4.2.0.RELEASE</org.springframework.version> <spring-security.version>3.2.7.RELEASE</spring-security.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Jackson JSON --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.5</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${spring-security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring-security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring-security.version}</version> </dependency> <!-- Spring OAUTH dependency --> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.0.12.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.5.RELEASE</version> </dependency> </dependencies> <build> <finalName>SpringRestServiceSecurityOauth</finalName> </build> </project>
We have added dependencies for Spring mvc ,spring security ,mysql,spring jdbc, Jackson and Junit in the above pom file.
Step 2
Update web.xml file with Dispatcher servlet and spring security filter
we have defined a dispatcher servlet in web.xml and mapped it by the URL pattern “/”
So just like any other servlet in web application,any request matching with the given pattern i.e “/” will be redirected to “Dispatcher servlet”.
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
- version="3.1">
- <display-name>Archetype Created Web Application</display-name>
- <servlet>
- <servlet-name>mvc-dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-beans.xml,
- /WEB-INF/spring-security.xml
- </param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>mvc-dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Loads Spring Security configuration file -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-beans.xml,
- /WEB-INF/spring-security.xml
- </param-value>
- </context-param>
- <!-- Spring Security filter -->
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-beans.xml, /WEB-INF/spring-security.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Loads Spring Security configuration file --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-beans.xml, /WEB-INF/spring-security.xml </param-value> </context-param> <!-- Spring Security filter --> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
We have provided the spring configuration file name to create and load the spring beans while starting the server.
Also we have provided Spring security config file to load the security related configuration.
we have also added filter for spring security which will delegate the request for authentication before processing the request.
Step 3
Create a Spring beans config file
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.2.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
- <context:component-scan base-package="com.kb" />
- <mvc:annotation-driven />
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <context:component-scan base-package="com.kb" /> <mvc:annotation-driven /> </beans>
Step 4
Create the spring security config file
- <?xml version="1.0" encoding="UTF-8" ?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
- http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">
- <!-- This is default url provided by spring to get the tokens(access and refresh) from OAuth -->
- <http pattern="/oauth/token" create-session="stateless"
- authentication-manager-ref="clientAuthenticationManager"
- xmlns="http://www.springframework.org/schema/security">
- <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
- <anonymous enabled="false" />
- <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
- <!-- include this only if you need to authenticate clients via request
- parameters -->
- <custom-filter ref="clientCredentialsTokenEndpointFilter"
- after="BASIC_AUTH_FILTER" />
- <access-denied-handler ref="oauthAccessDeniedHandler" />
- </http>
- <!-- This is where we tells spring security what URL should be protected
- and what roles have access to them -->
- <http pattern="/rest/api/**" create-session="never"
- entry-point-ref="oauthAuthenticationEntryPoint"
- access-decision-manager-ref="accessDecisionManager"
- xmlns="http://www.springframework.org/schema/security">
- <anonymous enabled="false" />
- <intercept-url pattern="/rest/api/**" access="ROLE_OAUTH_CLIENT" />
- <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
- <access-denied-handler ref="oauthAccessDeniedHandler" />
- </http>
- <bean id="oauthAuthenticationEntryPoint"
- class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
- <property name="realmName" value="sample" />
- </bean>
- <bean id="clientAuthenticationEntryPoint"
- class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
- <property name="realmName" value="sample/oauthClient" />
- <property name="typeName" value="Basic" />
- </bean>
- <bean id="oauthAccessDeniedHandler"
- class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
- <bean id="clientCredentialsTokenEndpointFilter"
- class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
- <property name="authenticationManager" ref="clientAuthenticationManager" />
- </bean>
- <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
- xmlns="http://www.springframework.org/schema/beans">
- <constructor-arg>
- <list>
- <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
- <bean class="org.springframework.security.access.vote.RoleVoter" />
- <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
- </list>
- </constructor-arg>
- </bean>
- <authentication-manager id="clientAuthenticationManager"
- xmlns="http://www.springframework.org/schema/security">
- <authentication-provider user-service-ref="clientDetailsUserService" />
- </authentication-manager>
- <!-- Here we have hard-coded user name and password details. We can replace this with a user defined service to get users
- credentials from DB -->
- <authentication-manager alias="authenticationManager"
- xmlns="http://www.springframework.org/schema/security">
- <authentication-provider>
- <user-service id="userDetailsService">
- <user name="kb" password="kb@1234" authorities="ROLE_OAUTH_CLIENT" />
- <user name="raj" password="raj@1234" authorities="ROLE_OAUTH_CLIENT" />
- </user-service>
- </authentication-provider>
- </authentication-manager>
- <bean id="clientDetailsUserService"
- class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
- <constructor-arg ref="clientDetails" />
- </bean>
- <!--We have used JDBC tokenstore to store the tokens, we can use In Memory token store for development purpose -->
- <bean id="tokenStore"
- class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
- <constructor-arg ref="jdbcTemplate" />
- </bean>
- <bean id="jdbcTemplate"
- class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/oauthdb"/>
- <property name="username" value="root"/>
- <property name="password" value="root"/>
- </bean>
- <!-- tokenServices bean for defining token based configurations, token validity etc -->
- <bean id="tokenServices"
- class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
- <property name="tokenStore" ref="tokenStore" />
- <property name="supportRefreshToken" value="true" />
- <property name="accessTokenValiditySeconds" value="120" />
- <property name="clientDetailsService" ref="clientDetails" />
- </bean>
- <bean id="requestFactory"
- class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
- <constructor-arg name="clientDetailsService" ref="clientDetails" />
- </bean>
- <bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
- <property name="tokenStore" ref="tokenStore"/>
- <property name="requestFactory" ref="requestFactory" />
- </bean>
- <oauth:authorization-server
- client-details-service-ref="clientDetails" token-services-ref="tokenServices"
- user-approval-handler-ref="userApprovalHandler">
- <oauth:authorization-code />
- <oauth:implicit />
- <oauth:refresh-token />
- <oauth:client-credentials />
- <oauth:password />
- </oauth:authorization-server>
- <oauth:resource-server id="resourceServerFilter"
- resource-id="sample" token-services-ref="tokenServices" />
- <oauth:client-details-service id="clientDetails">
- <!-- client -->
- <oauth:client client-id="trusted client"
- authorized-grant-types="password,refresh_token,client_credentials"
- authorities="ROLE_OAUTH_CLIENT" scope="read,write,trust" secret="secret" />
- <oauth:client client-id="trusted client with secret"
- authorized-grant-types="password,authorization_code,refresh_token,implicit"
- secret="somesecret" authorities="ROLE_OAUTH_CLIENT" />
- </oauth:client-details-service>
- <sec:global-method-security
- pre-post-annotations="enabled" proxy-target-class="true">
- <sec:expression-handler ref="oauthExpressionHandler" />
- </sec:global-method-security>
- <oauth:expression-handler id="oauthExpressionHandler" />
- <oauth:web-expression-handler id="oauthWebExpressionHandler" />
- </beans>
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd "> <!-- This is default url provided by spring to get the tokens(access and refresh) from OAuth --> <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security"> <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> <anonymous enabled="false" /> <http-basic entry-point-ref="clientAuthenticationEntryPoint" /> <!-- include this only if you need to authenticate clients via request parameters --> <custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" /> <access-denied-handler ref="oauthAccessDeniedHandler" /> </http> <!-- This is where we tells spring security what URL should be protected and what roles have access to them --> <http pattern="/rest/api/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint" access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security"> <anonymous enabled="false" /> <intercept-url pattern="/rest/api/**" access="ROLE_OAUTH_CLIENT" /> <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> <access-denied-handler ref="oauthAccessDeniedHandler" /> </http> <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> <property name="realmName" value="sample" /> </bean> <bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> <property name="realmName" value="sample/oauthClient" /> <property name="typeName" value="Basic" /> </bean> <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> <property name="authenticationManager" ref="clientAuthenticationManager" /> </bean> <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans"> <constructor-arg> <list> <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" /> <bean class="org.springframework.security.access.vote.RoleVoter" /> <bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> </list> </constructor-arg> </bean> <authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security"> <authentication-provider user-service-ref="clientDetailsUserService" /> </authentication-manager> <!-- Here we have hard-coded user name and password details. We can replace this with a user defined service to get users credentials from DB --> <authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security"> <authentication-provider> <user-service id="userDetailsService"> <user name="kb" password="kb@1234" authorities="ROLE_OAUTH_CLIENT" /> <user name="raj" password="raj@1234" authorities="ROLE_OAUTH_CLIENT" /> </user-service> </authentication-provider> </authentication-manager> <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <constructor-arg ref="clientDetails" /> </bean> <!--We have used JDBC tokenstore to store the tokens, we can use In Memory token store for development purpose --> <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore"> <constructor-arg ref="jdbcTemplate" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/oauthdb"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- tokenServices bean for defining token based configurations, token validity etc --> <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> <property name="tokenStore" ref="tokenStore" /> <property name="supportRefreshToken" value="true" /> <property name="accessTokenValiditySeconds" value="120" /> <property name="clientDetailsService" ref="clientDetails" /> </bean> <bean id="requestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory"> <constructor-arg name="clientDetailsService" ref="clientDetails" /> </bean> <bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler"> <property name="tokenStore" ref="tokenStore"/> <property name="requestFactory" ref="requestFactory" /> </bean> <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler"> <oauth:authorization-code /> <oauth:implicit /> <oauth:refresh-token /> <oauth:client-credentials /> <oauth:password /> </oauth:authorization-server> <oauth:resource-server id="resourceServerFilter" resource-id="sample" token-services-ref="tokenServices" /> <oauth:client-details-service id="clientDetails"> <!-- client --> <oauth:client client-id="trusted client" authorized-grant-types="password,refresh_token,client_credentials" authorities="ROLE_OAUTH_CLIENT" scope="read,write,trust" secret="secret" /> <oauth:client client-id="trusted client with secret" authorized-grant-types="password,authorization_code,refresh_token,implicit" secret="somesecret" authorities="ROLE_OAUTH_CLIENT" /> </oauth:client-details-service> <sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true"> <sec:expression-handler ref="oauthExpressionHandler" /> </sec:global-method-security> <oauth:expression-handler id="oauthExpressionHandler" /> <oauth:web-expression-handler id="oauthWebExpressionHandler" /> </beans>
In the above spring security xml file we have defined OAuth related configuration.
We have configured /oauth/token url for retrieving access and refresh token and configured authentication manager as clientAuthenticationManager
clientAuthenticationManager is defined as below
- <authentication-manager id="clientAuthenticationManager"
- xmlns="http://www.springframework.org/schema/security">
- <authentication-provider user-service-ref="clientDetailsUserService" />
- </authentication-manager>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security"> <authentication-provider user-service-ref="clientDetailsUserService" /> </authentication-manager>
This is pointing to clientDetailsUserService bean which is defined as below
- <bean id="clientDetailsUserService"
- class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
- <constructor-arg ref="clientDetails" />
- </bean>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <constructor-arg ref="clientDetails" /> </bean>
This is pointing to the actual client details service which is defined as below
- <oauth:authorization-server
- client-details-service-ref="clientDetails" token-services-ref="tokenServices"
- user-approval-handler-ref="userApprovalHandler">
- <oauth:authorization-code />
- <oauth:implicit />
- <oauth:refresh-token />
- <oauth:client-credentials />
- <oauth:password />
- </oauth:authorization-server>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler"> <oauth:authorization-code /> <oauth:implicit /> <oauth:refresh-token /> <oauth:client-credentials /> <oauth:password /> </oauth:authorization-server>
Here we have injected the token service to manage the refresh and access tokens.
Also defined possible Grant types in it.
we have defined token service bean with token details as below
- <bean id="tokenServices"
- class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
- <property name="tokenStore" ref="tokenStore" />
- <property name="supportRefreshToken" value="true" />
- <property name="accessTokenValiditySeconds" value="120" />
- <property name="clientDetailsService" ref="clientDetails" />
- </bean>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> <property name="tokenStore" ref="tokenStore" /> <property name="supportRefreshToken" value="true" /> <property name="accessTokenValiditySeconds" value="120" /> <property name="clientDetailsService" ref="clientDetails" /> </bean>
we have specified access token to be valid for 2 minutes.
We have also injected a tokenstore which will take care of storing the tokens.
we have defined tokenstore as JDBC token store as below
- <bean id="tokenStore"
- class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
- <constructor-arg ref="jdbcTemplate" />
- </bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore"> <constructor-arg ref="jdbcTemplate" /> </bean>
This will store the tokens in DB using JDBC and configuration for the JDBC is defined using JDBC template bean.
we have configured our rest service to be considered by OAuth using below tag
- <intercept-url pattern="/rest/api/**" access="ROLE_OAUTH_CLIENT" />
<intercept-url pattern="/rest/api/**" access="ROLE_OAUTH_CLIENT" />
This specifies that any URL with the pattern preceded by /rest/api will be intercepted by OAuth and Client should have the Role called ROLE_OAUTH_CLIENT to proceed further.
we have also defined authentication provider as user service with set of users along with their credentials and roles defined in it.
- <authentication-provider>
- <user-service id="userDetailsService">
- <user name="kb" password="kb@1234" authorities="ROLE_OAUTH_CLIENT" />
- <user name="raj" password="raj@1234" authorities="ROLE_OAUTH_CLIENT" />
- </user-service>
- </authentication-provider>
<authentication-provider> <user-service id="userDetailsService"> <user name="kb" password="kb@1234" authorities="ROLE_OAUTH_CLIENT" /> <user name="raj" password="raj@1234" authorities="ROLE_OAUTH_CLIENT" /> </user-service> </authentication-provider>
we are granting access to Rest services only for those users whose Role is ROLE_OAUTH_CLIENT as configured above.
Step 5
Execute below sql scripts(executed in MYSQL) to support JDBC Token store
- create table oauth_client_details (
- client_id VARCHAR(256) PRIMARY KEY,
- resource_ids VARCHAR(256),
- client_secret VARCHAR(256),
- scope VARCHAR(256),
- authorized_grant_types VARCHAR(256),
- web_server_redirect_uri VARCHAR(256),
- authorities VARCHAR(256),
- access_token_validity INTEGER,
- refresh_token_validity INTEGER,
- additional_information VARCHAR(4096),
- autoapprove VARCHAR(256)
- );
- create table oauth_client_token (
- token_id VARCHAR(256),
- token BLOB,
- authentication_id VARCHAR(256) PRIMARY KEY,
- user_name VARCHAR(256),
- client_id VARCHAR(256)
- );
- create table oauth_access_token (
- token_id VARCHAR(256),
- token BLOB,
- authentication_id VARCHAR(256),
- user_name VARCHAR(256),
- client_id VARCHAR(256),
- authentication BLOB,
- refresh_token VARCHAR(256)
- );
- create table oauth_refresh_token (
- token_id VARCHAR(256),
- token BLOB,
- authentication BLOB
- );
- create table oauth_code (
- code VARCHAR(256), authentication BLOB
- );
- create table oauth_approvals (
- userId VARCHAR(256),
- clientId VARCHAR(256),
- scope VARCHAR(256),
- status VARCHAR(10),
- expiresAt TIMESTAMP,
- lastModifiedAt TIMESTAMP
- );
- -- customized oauth_client_details table
- create table ClientDetails (
- appId VARCHAR(256) PRIMARY KEY,
- resourceIds VARCHAR(256),
- appSecret VARCHAR(256),
- scope VARCHAR(256),
- grantTypes VARCHAR(256),
- redirectUrl VARCHAR(256),
- authorities VARCHAR(256),
- access_token_validity INTEGER,
- refresh_token_validity INTEGER,
- additionalInformation VARCHAR(4096),
- autoApproveScopes VARCHAR(256)
- );
create table oauth_client_details ( client_id VARCHAR(256) PRIMARY KEY, resource_ids VARCHAR(256), client_secret VARCHAR(256), scope VARCHAR(256), authorized_grant_types VARCHAR(256), web_server_redirect_uri VARCHAR(256), authorities VARCHAR(256), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information VARCHAR(4096), autoapprove VARCHAR(256) ); create table oauth_client_token ( token_id VARCHAR(256), token BLOB, authentication_id VARCHAR(256) PRIMARY KEY, user_name VARCHAR(256), client_id VARCHAR(256) ); create table oauth_access_token ( token_id VARCHAR(256), token BLOB, authentication_id VARCHAR(256), user_name VARCHAR(256), client_id VARCHAR(256), authentication BLOB, refresh_token VARCHAR(256) ); create table oauth_refresh_token ( token_id VARCHAR(256), token BLOB, authentication BLOB ); create table oauth_code ( code VARCHAR(256), authentication BLOB ); create table oauth_approvals ( userId VARCHAR(256), clientId VARCHAR(256), scope VARCHAR(256), status VARCHAR(10), expiresAt TIMESTAMP, lastModifiedAt TIMESTAMP ); -- customized oauth_client_details table create table ClientDetails ( appId VARCHAR(256) PRIMARY KEY, resourceIds VARCHAR(256), appSecret VARCHAR(256), scope VARCHAR(256), grantTypes VARCHAR(256), redirectUrl VARCHAR(256), authorities VARCHAR(256), access_token_validity INTEGER, refresh_token_validity INTEGER, additionalInformation VARCHAR(4096), autoApproveScopes VARCHAR(256) );
Step 6
Create rest service which we want to secure
- package com.kb.rest.controllers;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.ResponseBody;
- import com.kb.rest.model.User;
- @Controller
- @RequestMapping("/rest/api")
- public class RestController {
- @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
- public @ResponseBody User getUserForId(@PathVariable ("id") int id) {
- User user = new User();
- user.setId(id);
- user.setName("John");
- user.setAge(45);
- return user;
- }
- }
package com.kb.rest.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.kb.rest.model.User; @Controller @RequestMapping("/rest/api") public class RestController { @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public @ResponseBody User getUserForId(@PathVariable ("id") int id) { User user = new User(); user.setId(id); user.setName("John"); user.setAge(45); return user; } }
we have exposed one method for retrieving the user based on id.
Step 7
Build and deploy the project
Step 8
Let’s access the rest service using Postman client
Access rest service without authentication info using below url
http://localhost:8080/SpringRestServiceSecurityOauth/rest/api/user/1
Select GET method
We can see that access is denied due to unauthorization as we are accessing the secured service without access token.
Let’s get the access token using below url
http://localhost:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials
add user name and password in the header as shown below
Authrorization will be added to the header automatically
We can see that access_token will expire in 119 seconds.
Now access the rest service using access token before it gets expired.
http://localhost:8080/SpringRestServiceSecurityOauth/rest/api/user/1?access_token=9409a4a0-0887-46f5-a36e-9b1de71699df
Note:
We will not get refresh token if the grant type is client_credentials and hence we are not getting refresh_token from OAuth.
i need to validate the token in separate microservice when login and token response provided by another spring boot
Hello, your snippet is very useful, i’m trying this whithout JDBC TokenStore, but i don’t how i can do it, i prefer to use a simple “Memory Token”
i try to set :
In this class, I implement TokenStore :
public class MyTokenStore implements TokenStore
{
…
public OAuth2AccessToken readAccessToken(String accessToken) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken) ;
At this step i have the token in GUID format …
Any ideas ? Help is welcome 🙂
Thanks
Thank you Karibasappa G C (KB) for writing a marabous article. This is what i was searching xml based oauth token example. I have not implemented this yet but looks like this article is having all the answers which required to implement it.
May god give you the strong mindset so that you can fulfill your every desire what you want to achieve in your life. I salute you.
marvelous*
hi I just copy your code and try to run it.
then got the HTTP Status 404 – Not Found
Description: The origin server did not find a current representation for the target resource or is not willing to disclose that one exists
Apache Tomcat/8.5.39
can you plzz expalin the use of realmname and typename in entry point…
I tried on OAuth2 (2.0.12) and I got this error.
{
“error”: “unauthorized”,
“error_description”: “There is no client authentication. Try adding an appropriate authentication filter.”
}
Main Problem: on OAuth2AuthenticationProcessingFilter.java, there are as below on doFilter:
if (stateless && isAuthenticated()) {
if (debug) {
logger.debug(“Clearing security context.”);
}
SecurityContextHolder.clearContext();
}
But, if I tried on OAuth2(2.0.4), It worked. There are not as above on doFilter
Thanks Karibasappa,
I also tried to do same, but getting same error, bad credentials. Could you please help me with this, am really getting stuck. It should work with user details , not auth client , Please correct me.
Thanks K,
Your blog really helped me a lot to understand the concept.
When I run the code snippets provided by you, I showed the same issue what Francis was facing.
Please help me with this.
I have some employees .Whenever they do log in , it needs to generate different access token for different users .Here based on client id , the same tokens are getting returned . Please help me if you have figured out?
@Ujitha Sudasingha, @AK, @Mukesh, @Vikram, @Ha Nguyen, @Francis
Hi all,
If you are facing the same error as below :
{
“error”: “unauthorized”,
“error_description”: “Bad credentials”
}
Because you are not passing Authorization header properly, as it is mentioned in “spring-security.xml” file as below :
So to achieve expected behavior you have to provide appropriate client credentials as below :
In POSTMAN
1) Select Post Method & Provide URL as (http://localhost:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials)
2) Authorization tab –> Select Type as “Basic Auth”
3) And then in right panel provide
Username : trusted client
Password : secret
4) Finally Click on Send.
It works for me, hope it will work for you without any error.
Dear Karibasappa G C (KB),
I got the below error every time.Please give me a solution 🙁
{
“error”: “unauthorized”,
“error_description”: “Bad credentials”
}
Is there any updates ? How u solved this issue ? I m getting same error
Could you please tell me how to set difference token on difference use please.
And I also have to add my client id and secret to create a authentication header and add username/password to request header, too. if I have 2 user, their token are the same, please help.
Hi Karibasappa
please share ur email id so i can send code to u.
Hello,
I was also facing this issue.
I resolved it as below :
1) Gave a client id and secret –
2) In postman doing a POST request as below –
http://localhost:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials&client_id=client_id&client_secret=client_secret&scope=read,write,trust
{
“access_token”: “aba451b0-89bd-4f29-b461-3a0d76406606”,
“token_type”: “bearer”,
“expires_in”: 119,
“scope”: “read,write,trust”
}
Great , It has worked for me without those query params.
But good info, People can refer this if they are facing such issue.
Thanks again !!
what is the authorization header there
{
“username”: “kb”
“password”: “kb@1234”
“Authorization”: “Basic a2I6a2JAMTIzNA==”
}
Can you give me your script inserts?
Hello Karibasappa,
I have just built your sample code to war file and deploy to tomcat 8, and I get the same issue with Francis. Would you mind taking a look? Thank you very much.
I send POST request: http://192.168.1.33:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials
Header
{
“username”: “kb”
“password”: “kb@1234”
“Authorization”: “Basic a2I6a2JAMTIzNA==”
}
And I get response:
{
“error”: “unauthorized”,
“error_description”: “Bad credentials”
}
Hi,
Is it same when you use Rest client also , try any Rest clients other than Postman.
Hello Karibasappa,
Thanks for your response. I did try two Rest clients: Postman and Advanced REST but not success.
Could you test your sample source code again to see if it works? Thank you very much.
Hope you have executed DB queries as well.
I have got the output when i ran this project, and if i don’t get output generally I wont post such articles.
If any typo happens , I need to check it…Please send me your code in zip I will check it once and update you.
Thank you!!
Yeah, thank you very much for your quick response.
Here are steps I have tried:
1. Download your project from this link: http://javainsimpleway.com/wp-content/uploads/2016/12/SpringRestServiceSecurityOauth.zip
2. Configure database url.
3. Deploy using mvn package and test.
Result: Unfortunately it does not work.
When I send this POST request: http://192.168.1.33:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials, I always get the unauthorized error.
ok I will try the same and update you
Thanks Karibasappa very much.
Hi,
I am running this: http://localhost:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials
and credentials are as you mentioned, but it is not work an gives back to me:
{
“error”: “unauthorized”,
“error_description”: “Bad credentials”
}
Check whether you have given the same credentials details in the below tag of Oauth security config xml
Thanks for response. Actually i am big fan of your code style.
Yes I have credentials details in authentication-provider. I run the code in Intellij. I also add the javax-servlet.3 in the maven repository. I dont know why it is not works?
Thank you Francis !!
All the spring security and Oauth dependencies are downloaded from maven ?
And as shown in the figure, authorization is added in the header as shown in the above screenshot and hope you have selected POST method right ?
Yes Karibasappa,
All are as you mentioned. First section which not requires the credentials http://localhost:8080/SpringRestServiceSecurityOauth/rest/api/user/1 is works well. but for second part http://localhost:8080/SpringRestServiceSecurityOauth/oauth/token?grant_type=client_credentials it not work for me. I used the postman and insert the header as you mentioned.
I have another question, Why your encrypted method different from me? I get the Basic a2I6a2JAMTIzNA== as encryption in postman. did you selected other encrypted method that base64?
No I have used the same
Then its strange, it should work , can you send me your code, I will try to look into it in detail and i will try if i can reproduce it.