天天看點

架構師之路-如何建構rest接口的安全性通路(dubbox+oatuh2+rest)

建立oauth2認證需要的資料庫及資料表結構

CREATE SCHEMA IF NOT EXISTS `oauth2` DEFAULT CHARACTER SET utf8 ;

USE `oauth2` ;

-- -----------------------------------------------------

-- Table `oauth2`.`clientdetails`

CREATE TABLE IF NOT EXISTS `oauth2`.`clientdetails` (

  `appId` VARCHAR(128) NOT NULL,

  `resourceIds` VARCHAR(256) NULL DEFAULT NULL,

  `appSecret` VARCHAR(256) NULL DEFAULT NULL,

  `scope` VARCHAR(256) NULL DEFAULT NULL,

  `grantTypes` VARCHAR(256) NULL DEFAULT NULL,

  `redirectUrl` VARCHAR(256) NULL DEFAULT NULL,

  `authorities` VARCHAR(256) NULL DEFAULT NULL,

  `access_token_validity` INT(11) NULL DEFAULT NULL,

  `refresh_token_validity` INT(11) NULL DEFAULT NULL,

  `additionalInformation` VARCHAR(4096) NULL DEFAULT NULL,

  `autoApproveScopes` VARCHAR(256) NULL DEFAULT NULL,

  PRIMARY KEY (`appId`))

ENGINE = InnoDB

DEFAULT CHARACTER SET = utf8;

-- Table `oatuh2`.`oauth_access_token`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_access_token` (

  `token_id` VARCHAR(256) NULL DEFAULT NULL,

  `token` BLOB NULL DEFAULT NULL,

  `authentication_id` VARCHAR(128) NOT NULL,

  `user_name` VARCHAR(256) NULL DEFAULT NULL,

  `client_id` VARCHAR(256) NULL DEFAULT NULL,

  `authentication` BLOB NULL DEFAULT NULL,

  `refresh_token` VARCHAR(256) NULL DEFAULT NULL,

  PRIMARY KEY (`authentication_id`))

-- Table `oatuh2`.`oauth_approvals`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_approvals` (

  `userId` VARCHAR(256) NULL DEFAULT NULL,

  `clientId` VARCHAR(256) NULL DEFAULT NULL,

  `status` VARCHAR(10) NULL DEFAULT NULL,

  `expiresAt` DATETIME NULL DEFAULT NULL,

  `lastModifiedAt` DATETIME NULL DEFAULT NULL)

-- Table `oatuh2`.`oauth_client_details`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_details` (

  `client_id` VARCHAR(128) NOT NULL,

  `resource_ids` VARCHAR(256) NULL DEFAULT NULL,

  `client_secret` VARCHAR(256) NULL DEFAULT NULL,

  `authorized_grant_types` VARCHAR(256) NULL DEFAULT NULL,

  `web_server_redirect_uri` VARCHAR(256) NULL DEFAULT NULL,

  `additional_information` VARCHAR(4096) NULL DEFAULT NULL,

  `autoapprove` VARCHAR(256) NULL DEFAULT NULL,

  PRIMARY KEY (`client_id`))

-- Table `oatuh2`.`oauth_client_token`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_token` (

  `token_id` VARCHAR(256) NULL DEFAULT NULL,

-- Table `oatuh2`.`oauth_code`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_code` (

  `code` VARCHAR(256) NULL DEFAULT NULL,

  `authentication` BLOB NULL DEFAULT NULL)

-- Table `oatuh2`.`oauth_refresh_token`

CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_refresh_token` (

>>前提:  使用Maven來管理項目; spring-security-oauth的版本号為 2.0.10.RELEASE

1. 添加Maven dependencies; 以下隻列出了主要的

<dependency>  

    <groupId>org.springframework.securitygroupId>  

    <artifactId>spring-security-coreartifactId>  

    <version>${spring.security.version}version>  

dependency>  

    <artifactId>spring-security-webartifactId>  

    <artifactId>spring-security-taglibsartifactId>  

    <artifactId>spring-security-aclartifactId>  

    <artifactId>spring-security-cryptoartifactId>  

    <artifactId>spring-security-configartifactId>  

    <groupId>org.springframework.security.oauthgroupId>  

    <artifactId>spring-security-oauth2artifactId>  

    <version>1.0.5.RELEASEversion>  

2. web.xml配置; 這一步與隻使用Spring Security的配置一樣.

pre><pre code_snippet_id="73897" snippet_file_name="blog_20131119_2_2257675" name="code" class="html">    <filter>  

        <filter-name>springSecurityFilterChainfilter-name>  

        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>  

    filter>  

    <filter-mapping>  

        <url-pattern>/*url-pattern>  

    filter-mapping>  

    <context-param>  

        <param-name>contextConfigLocationparam-name>  

        <param-value>classpath:spring/*.xmlparam-value>  

    context-param>  

    <listener>  

        <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>  

    listener>  

    <servlet>  

        <servlet-name>hyservlet-name>  

        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>  

        <load-on-startup>2load-on-startup>  

    servlet>  

    <servlet-mapping>  

        <url-pattern>/url-pattern>  

    servlet-mapping>  

對于Spring MVC, 需要配置檔案hy-servlet.xml, 該檔案不是這兒關注的(忽略); 

在classpath建立spring目錄, 在該目錄裡建立 security.xml 檔案, 這是所有步驟配置的重點.

3.security.xml的配置; 重點開始.

3.1 起用注解; TokenEndpoint與AuthorizationEndpoint需要

<mvc:annotation-driven/>  

<mvc:default-servlet-handler/>  

3.2  TokenServices 配置

   1). TokenStore, 使用JdbcTokenStore, 将token資訊存放資料庫, 需要提供一個dataSource對象; 也可使用InMemoryTokenStore存于記憶體中

<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">  

    <beans:constructor-arg index="0" ref="dataSource"/>  

beans:bean>  

  2).TokenServices; 需要注入TokenStore

<beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">  

    <beans:property name="tokenStore" ref="tokenStore"/>  

    <beans:property name="supportRefreshToken" value="true"/>  

      如果允許重新整理token 請将supportRefreshToken 的值設定為true, 預設為不允許

3.3 ClientDetailsService 配置, 使用JdbcClientDetailsService, 也需要提供dataSource, 替換demo中直接配置在配置檔案中

<beans:bean id="clientDetailsService" class="org.springframework.security.oauth2.provider.JdbcClientDetailsService">  

3.4 ClientDetailsUserDetailsService配置, 該類實作了Spring security中 UserDetailsService 接口

<beans:bean id="oauth2ClientDetailsUserService"  

            class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">  

    <beans:constructor-arg ref="clientDetailsService"/>  

3.5 OAuth2AuthenticationEntryPoint配置

<beans:bean id="oauth2AuthenticationEntryPoint"  

            class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>  

3.6 oauth2 AuthenticationManager配置; 在整個配置中,有兩個AuthenticationManager需要配置

<authentication-manager id="oauth2AuthenticationManager">  

    <authentication-provider user-service-ref="oauth2ClientDetailsUserService"/>  

authentication-manager>  

第二個AuthenticationManager用于向擷取UserDetails資訊, 

<authentication-manager alias="authenticationManager">  

    <authentication-provider user-service-ref="userService">  

        <password-encoder hash="md5"/>  

    authentication-provider>  

userService是一個實作UserDetailsService的Bean

3.7 OAuth2AccessDeniedHandler配置, 實作AccessDeniedHandler接口

<beans:bean id="oauth2AccessDeniedHandler"  

            class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>  

3.8 UserApprovalHandler配置, 這兒使用DefaultUserApprovalHandler, 這裡是實作client是否可信任的關鍵點,你可以擴充該接口來自定義approval行為

<beans:bean id="oauthUserApprovalHandler" class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler">  

3.9 authorization-server配置, 核心

<oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"  

                             user-approval-handler-ref="oauthUserApprovalHandler">  

    <oauth2:authorization-code/>  

    <oauth2:implicit/>  

    <oauth2:refresh-token/>  

    <oauth2:client-credentials/>  

    <oauth2:password/>  

oauth2:authorization-server>  

該元素裡面的每個标簽可設定每一種authorized-grant-type的行為. 如disable refresh-token的配置為

<oauth2:refresh-token disabled="true"/>  

3.10 Oauth2 AccessDecisionManager配置, 這兒在預設的Spring Security AccessDecisionManager的基礎上添加了ScopeVoter

<beans:bean id="oauth2AccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">  

    <beans:constructor-arg>  

        <beans:list>  

            <beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter"/>  

            <beans:bean class="org.springframework.security.access.vote.RoleVoter"/>  

            <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>  

        beans:list>  

    beans:constructor-arg>  

3.11 resource-server配置, 這兒定義兩咱不同的resource

<oauth2:resource-server id="unityResourceServer" resource-id="unity-resource" token-services-ref="tokenServices"/>  

<oauth2:resource-server id="mobileResourceServer" resource-id="mobile-resource" token-services-ref="tokenServices"/>  

注意: 每個resource-id的值必須在對應的ClientDetails中resourceIds值中存在

3.12 ClientCredentialsTokenEndpointFilter配置, 該Filter将作用于Spring Security的chain 鍊條中

<beans:bean id="clientCredentialsTokenEndpointFilter"  

            class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">  

    <beans:property name="authenticationManager" ref="oauth2AuthenticationManager"/>  

3.13 /oauth/token 的http 配置, 用于監聽該URL的請求, 核心

<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="oauth2AuthenticationManager"  

      entry-point-ref="oauth2AuthenticationEntryPoint">  

    <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>  

    <anonymous enabled="false"/>  

    <http-basic entry-point-ref="oauth2AuthenticationEntryPoint"/>  

    <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>  

    <access-denied-handler ref="oauth2AccessDeniedHandler"/>  

http>  

3.14 針對不同resource的http配置, 由于上面配置了兩個resource, 這兒也配置兩個

<http pattern="/unity/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint"  

      access-decision-manager-ref="oauth2AccessDecisionManager">  

    <intercept-url pattern="/unity/**" access="ROLE_UNITY,SCOPE_READ"/>  

    <custom-filter ref="unityResourceServer" before="PRE_AUTH_FILTER"/>  

<http pattern="/m/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint"  

    <intercept-url pattern="/m/**" access="ROLE_MOBILE,SCOPE_READ"/>  

    <custom-filter ref="mobileResourceServer" before="PRE_AUTH_FILTER"/>  

注意每一個http對應不同的resourceServer. access-decison-manager-ref對應Oauth的AccessDecisionManager

3.15 預設的http配置,給/oauth/** 設定權限

<http access-denied-page="/login.jsp?authorization_error=2" disable-url-rewriting="true"  

      authentication-manager-ref="authenticationManager">  

    <intercept-url pattern="/oauth/**" access="ROLE_USER,ROLE_UNITY,ROLE_MOBILE"/>  

    <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>  

    <form-login authentication-failure-url="/login.jsp?authentication_error=1" default-target-url="/index.jsp"  

                login-page="/login.jsp" login-processing-url="/login.do"/>  

    <logout logout-success-url="/index.jsp" logout-url="/logout.do"/>  

    <anonymous/>  

到此, securiy.xml 配置完畢.

當然,還有些額外的工作你需要做, 如配置dataSource, 建立資料庫, 添加使用者使用者資訊, 管理ClientDetails等等.

Oauth相關的資料都是存放在資料庫, 我們就可以根據表結果建立domain來實作管理.