spring-security-oauth2使用

差不多有两周的时间了,都在玩这个spring-security-oauth2,网上有不少资料,也看了不少,但始终自己调不通,最后通过一个网上可以调通的例子与我本机进行逐行debug对比,终于发现了问题。

这里记录一下这两周的心得。

参考资料(未完全列出):

http://patrick002.iteye.com/blog/2207795

http://wwwcomy.iteye.com/blog/2230265

1、误区

网上很多的资料,包括成功的案例,大都不是最新的oauth,然而最新的oauth却跟之前的有很多不同,所以借鉴网上的案例之前,要先确保环境跟它一样,否则可能会跑不通。

问题是根据 Spring security oauth2最简单入门环境搭建--二、干货 一文发现的,因为这个例子我放到本机居然跑通了,但我自己做的demo写的一样居然跑不通。

2、环境

备注:这里要特别注意当前所使用的环境,即各种jar的版本

我的问题主要就出在spring-security-oauth2-2.1.0.jar,而上面文章里用的是spring-security-oauth2-2.0.2.jar,这两个jar中的差距太大了,会直接影响oauth的整个配置

附:我自己的pom.xml,基本上都是用的目前官网最新的jar

<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>com.flysand</groupId>
  <artifactId>Test</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>Test Maven Webapp</name>
  <url>http://maven.apache.org</url>


  <properties>

    <spring-version>4.3.7.RELEASE</spring-version>
    <spring-security-version>4.2.2.RELEASE</spring-security-version>
    <spring-security-oauth2-version>2.1.0.RELEASE</spring-security-oauth2-version>
    <mybatis-version>3.4.2</mybatis-version>
    <mybatis-spring-version>1.3.1</mybatis-spring-version>
    <druid-version>1.0.29</druid-version>
    <fastjson-version>1.2.30</fastjson-version>
    <logback-version>1.2.2</logback-version>
    <mysql-version>5.1.41</mysql-version>
    <servlet-api-version>4.0.0-b03</servlet-api-version>
    <jstl-version>1.2</jstl-version>
    <commons-codec-version>1.10</commons-codec-version>
    <jackson-version>2.8.7</jackson-version>
    <pageHelper-version>4.2.1</pageHelper-version>

  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql-version}</version>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>${commons-codec-version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson-version}</version>
    </dependency>
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>${pageHelper-version}</version>
    </dependency>

    <!--spring  springMvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring-version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring-version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring-version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <!-- spring security -->
    <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>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</artifactId>
      <version>${spring-security-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.security.oauth</groupId>
      <artifactId>spring-security-oauth2</artifactId>
      <version>${spring-security-oauth2-version}</version>
    </dependency>

    <!--mybatis 数据库相关-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis-version}</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>${mybatis-spring-version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid-version}</version>
    </dependency>

    <!--tools-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson-version}</version>
    </dependency>

    <!--logback -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>${logback-version}</version>
    </dependency>

    <!--servlet jstl -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet-api-version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>${jstl-version}</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>Test</finalName>
  </build>
</project>

上面两个jar包的主要区别(影响我正常跑通的地方)

1> 新版本的/oauth/token验证不再支持GET请求

TokenEndpoint.java对比,新版本增加了对GET请求的禁止访问
/*
 * Copyright 2002-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.oauth2.provider.endpoint;

import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2RequestValidator;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * <p>
 * Endpoint for token requests as described in the OAuth2 spec. Clients post requests with a <code>grant_type</code>
 * parameter (e.g. "authorization_code") and other parameters as determined by the grant type. Supported grant types are
 * handled by the provided {@link #setTokenGranter(org.springframework.security.oauth2.provider.TokenGranter) token
 * granter}.
 * </p>
 * 
 * <p>
 * Clients must be authenticated using a Spring Security {@link Authentication} to access this endpoint, and the client
 * id is extracted from the authentication token. The best way to arrange this (as per the OAuth2 spec) is to use HTTP
 * basic authentication for this endpoint with standard Spring Security support.
 * </p>
 * 
 * @author Dave Syer
 * 
 */
@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {

    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();

  //默认初始化允许的requestMethod只能是POST
private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST)); @RequestMapping(value = "/oauth/token", method=RequestMethod.GET) public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    //当请求为GET时,抛异常
if (!allowedRequestMethods.contains(HttpMethod.GET)) { throw new HttpRequestMethodNotSupportedException("GET"); } return postAccessToken(principal, parameters); } @RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { if (!(principal instanceof Authentication)) { throw new InsufficientAuthenticationException( "There is no client authentication. Try adding an appropriate authentication filter."); } String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient); if (clientId != null && !clientId.equals("")) { // Only validate the client details if a client authenticated during this // request. if (!clientId.equals(tokenRequest.getClientId())) { // double check to make sure that the client ID in the token request is the same as that in the // authenticated client throw new InvalidClientException("Given client ID does not match authenticated client"); } } if (authenticatedClient != null) { oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); } if (!StringUtils.hasText(tokenRequest.getGrantType())) { throw new InvalidRequestException("Missing grant type"); } if (tokenRequest.getGrantType().equals("implicit")) { throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); } if (isAuthCodeRequest(parameters)) { // The scope was requested or determined during the authorization step if (!tokenRequest.getScope().isEmpty()) { logger.debug("Clearing scope of incoming token request"); tokenRequest.setScope(Collections.<String> emptySet()); } } if (isRefreshTokenRequest(parameters)) { // A refresh token has its own default scopes, so we should ignore any added by the factory here. tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE))); } OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); if (token == null) { throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType()); } return getResponse(token); } /** * @param principal the currently authentication principal * @return a client id if there is one in the principal */ protected String getClientId(Principal principal) { Authentication client = (Authentication) principal; if (!client.isAuthenticated()) { throw new InsufficientAuthenticationException("The client is not authenticated."); } String clientId = client.getName(); if (client instanceof OAuth2Authentication) { // Might be a client and user combined authentication clientId = ((OAuth2Authentication) client).getOAuth2Request().getClientId(); } return clientId; }
  //增加GET方法不支持的异常处理方法 @ExceptionHandler(HttpRequestMethodNotSupportedException.
class) public ResponseEntity<OAuth2Exception> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception { logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); return getExceptionTranslator().translate(e); } @ExceptionHandler(Exception.class) public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception { logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); return getExceptionTranslator().translate(e); } @ExceptionHandler(ClientRegistrationException.class) public ResponseEntity<OAuth2Exception> handleClientRegistrationException(Exception e) throws Exception { logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); return getExceptionTranslator().translate(new BadClientCredentialsException()); } @ExceptionHandler(OAuth2Exception.class) public ResponseEntity<OAuth2Exception> handleException(OAuth2Exception e) throws Exception { logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); return getExceptionTranslator().translate(e); } private ResponseEntity<OAuth2AccessToken> getResponse(OAuth2AccessToken accessToken) { HttpHeaders headers = new HttpHeaders(); headers.set("Cache-Control", "no-store"); headers.set("Pragma", "no-cache"); return new ResponseEntity<OAuth2AccessToken>(accessToken, headers, HttpStatus.OK); } private boolean isRefreshTokenRequest(Map<String, String> parameters) { return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null; } private boolean isAuthCodeRequest(Map<String, String> parameters) { return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null; } public void setOAuth2RequestValidator(OAuth2RequestValidator oAuth2RequestValidator) { this.oAuth2RequestValidator = oAuth2RequestValidator; } public void setAllowedRequestMethods(Set<HttpMethod> allowedRequestMethods) { this.allowedRequestMethods = allowedRequestMethods; } }

2> spring-security-oauth2.xsd

spring-security-oauth配置文件所依赖的命名空间。

<!-- oauth2 授权服务器 -->
    <oauth2:authorization-server client-details-service-ref="clientDetailsService"
                                 token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler">
        <oauth2:authorization-code/>
        <oauth2:implicit/>
        <oauth2:refresh-token/>
        <oauth2:client-credentials/>
     <--password验证的时候,配置验证所用的(自定义)认证管理器,否则新版本会直接调用上面的oauth的认证管理器--> <oauth2:password authentication-manager-ref="authenticationManager" /> </oauth2:authorization-server>
  <!--自定义的用户验证的认证管理器,注意这里的id,上面的案例写成了alias别名,貌似有问题-->
  <
security:authentication-manager id="authenticationManager">
    <!--这里可配置user-service,jdbc-user-service(默认提供了jdbc数据库相关联的用户验证查询)-->
<!--<security:authentication-provider user-service-ref="userService"> &lt;!&ndash;用户角色权限信息配置在userService里&ndash;&gt; </security:authentication-provider>--> <!--<security:authentication-provider> <security:user-service> &lt;!&ndash;指定当前用户的信息及权限&ndash;&gt; <security:user name="user" authorities="ROLE_USER" password="user"/> <security:user name="admin" authorities="IS_AUTHENTICATED_FULLY" password="admin"/> <security:user name="test1" authorities="IS_AUTHENTICATED_FULLY" password="123456"/> </security:user-service> </security:authentication-provider>--> <security:authentication-provider user-service-ref="userService"> </security:authentication-provider> </security:authentication-manager> <bean id="userService" class="com.flysand.oauth.MyUserService"/>

因为限制限制GET请求,而spring-security本身自带csrf过滤器会自动拦截所有的POST请求,因此需要单独加一个例外的requestMatcher用来except oauth的请求。

自定义的requestMatcher

CsrfSecurityMatcher.java
package com.flysand.matcher;

import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Title:CsrfSecurityMatcher.java
 * Location:com.flysand.matcher
 * Author:flysand
 * Date:2017年04月13 16:07:40
 * Description:
 **/
public class CsrfSecurityMatcher implements RequestMatcher {

    private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");

    private List<String> execludeUrls;

    public boolean matches(HttpServletRequest request) {

        if(execludeUrls !=null && execludeUrls.size()>0){
            String servletPath =request.getServletPath();
            for(String url : execludeUrls){
                if(servletPath.contains(url)){
                    return false;
                }
            }
        }

        return !allowedMethods.matcher(request.getMethod()).matches();
    }


    public List<String> getExecludeUrls() {
        return execludeUrls;
    }

    public void setExecludeUrls(List<String> execludeUrls) {
        this.execludeUrls = execludeUrls;
    }
}

spring-security.xml中的配置

    <security:http pattern="/oauth/token" create-session="stateless"
                   authentication-manager-ref="clientAuthenticationManager">
        <security:intercept-url pattern="/oauth/token" access="hasRole('ROLE_USER')"/>
        <!--spring security 4 默认添加csrfFilter过滤器,限制所有post请求-->
        <security:csrf request-matcher-ref="csrfSecurityRequestMatcher"/>
        <security:anonymous enabled="false"/>
        <security:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
        <security:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
        <security:access-denied-handler ref="accessDeniedHandler"/>
    </security:http>

    <!--自定义requestMatcher用于解除list列表里的post请求的限制-->
    <bean id="csrfSecurityRequestMatcher" class="com.flysand.matcher.CsrfSecurityMatcher">
        <property name="execludeUrls">
            <list>
                <value>/oauth/</value>
            </list>
        </property>
    </bean>

这样基本上就可以了。

其他配置基本上跟参考的资料里面配置一样

附完整的spring-security.xml

<?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:oauth2="http://www.springframework.org/schema/security/oauth2"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/security
            http://www.springframework.org/schema/security/spring-security.xsd
            http://www.springframework.org/schema/security/oauth2
            http://www.springframework.org/schema/security/spring-security-oauth2.xsd">


    <!--spring security 配置 -->

    <!--token 存储方式  InMemoryTokenStore内存  JDBC  jwt 等方式-->
    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/>
    <!--token 业务处理 这里用默认的  可以自定义-->
    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore"/>
        <property name="supportRefreshToken" value="true"/>
        <property name="clientDetailsService" ref="clientDetailsService"/>
    </bean>

    <!--client  认证接入点 -->
    <bean id="clientAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
    <!--访问拒绝的handler -->
    <bean id="accessDeniedHandler"
          class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>

    <!--A default user approval handler that doesn't remember any decisions.-->
    <bean id="userApprovalHandler"
          class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler"/>

    <!---clientdetails =-->
    <!--<bean id="clientDetailsService" class="com.flysand.web.**"/>-->
    <oauth2:client-details-service id="clientDetailsService">
        <oauth2:client client-id="client" authorized-grant-types="password" authorities="IS_AUTHENTICATED_FULLY"
                       secret="secret" scope="read,write,trust"/>
        <oauth2:client client-id="test1" authorities="IS_AUTHENTICATED_FULLY" authorized-grant-types="password" secret="123456" scope="read"/>
        <oauth2:client client-id="test" authorities="ROLE_USER" authorized-grant-types="password" secret="123456" scope="read"/>
        <oauth2:client client-id="user" authorities="ROLE_USER" authorized-grant-types="password" secret="user" scope="read,write"/>
    </oauth2:client-details-service>
    <!--<bean id="clientDetailsService" class="com.flysand.oauth.MyClientDetailsService"/>-->


    <bean id="clientDetailsUserDetailsService"
          class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="clientDetailsService"/>
    </bean>

    <!--client 认证 管理器 -->
    <security:authentication-manager id="clientAuthenticationManager">
        <security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
    </security:authentication-manager>

    <!--client credential endpoint filter
    A filter and authentication endpoint for the OAuth2 Token Endpoint-->
    <bean id="clientCredentialsTokenEndpointFilter"
          class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="clientAuthenticationManager"/>
    </bean>


    <!-- oauth2 授权服务器 -->
    <oauth2:authorization-server client-details-service-ref="clientDetailsService"
                                 token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler">
        <oauth2:authorization-code/>
        <oauth2:implicit/>
        <oauth2:refresh-token/>
        <oauth2:client-credentials/>
        <oauth2:password authentication-manager-ref="authenticationManager" />
    </oauth2:authorization-server>

    <!--http
    stateless 无状态session,默认create,每次都创建session会给服务器压力很大,
    不保存session状态,每次访问需要重新认证,即带user token
    详解http://www.cnblogs.com/Mainz/archive/2013/08/01/3230077.html-->
    <security:http pattern="/oauth/token" create-session="stateless"
                   authentication-manager-ref="clientAuthenticationManager">
        <security:intercept-url pattern="/oauth/token" access="hasRole('ROLE_USER')"/>
        <!--spring security 4 默认添加csrfFilter过滤器,限制所有post请求-->
        <security:csrf request-matcher-ref="csrfSecurityRequestMatcher"/>
        <security:anonymous enabled="false"/>
        <security:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
        <security:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
        <security:access-denied-handler ref="accessDeniedHandler"/>
    </security:http>

    <!--自定义requestMatcher用于解除list列表里的post请求的限制-->
    <bean id="csrfSecurityRequestMatcher" class="com.flysand.matcher.CsrfSecurityMatcher">
        <property name="execludeUrls">
            <list>
                <value>/oauth/</value>
            </list>
        </property>
    </bean>
    <!-- user setting -->
    <!--<bean id="userService" class="com.flysand.service.UserService"/>-->

    <security:authentication-manager id="authenticationManager">
        <!--<security:authentication-provider user-service-ref="userService">
            &lt;!&ndash;用户角色权限信息配置在userService里&ndash;&gt;
        </security:authentication-provider>-->
        <!--<security:authentication-provider>
            <security:user-service>
                &lt;!&ndash;指定当前用户的信息及权限&ndash;&gt;
                <security:user name="user" authorities="ROLE_USER" password="user"/>
                <security:user name="admin" authorities="IS_AUTHENTICATED_FULLY" password="admin"/>
                <security:user name="test1" authorities="IS_AUTHENTICATED_FULLY" password="123456"/>
            </security:user-service>
        </security:authentication-provider>-->
        <security:authentication-provider user-service-ref="userService">

        </security:authentication-provider>
    </security:authentication-manager>
    <bean id="userService" class="com.flysand.oauth.MyUserService"/>

    <!--资源服务器-->
    <oauth2:resource-server id="myResourceService" resource-id="myresource" token-services-ref="tokenServices"/>

    <!--访问决策管理器-->
    <!--决策投票器 AffirmativeBased 一票通过 ConsensusBased少数服从多数 UnanimousBased全票通过
     其中 ConsensusBased少数服从多数——当投票数相等时,
     默认的private boolean allowIfEqualGrantedDeniedDecisions = true属性起作用,即只要不为0默认验证通过-->
    <!--<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <constructor-arg>
            <list>
                &lt;!&ndash;投票器(权限验证规则),RoleVoter验证角色,AuthenticatedVoter当角色不存在时验证,
                包括IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED,IS_AUTHENTICATED_ANONYMOUSLY
                WebExpressionVoter表达式投票器,必须设置,不然自定义的&ndash;&gt;
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
                <bean class="org.springframework.security.access.vote.RoleVoter"/>
                <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
            </list>
        </constructor-arg>
    </bean>-->
    <!--自定义accessDecisionManager-->
    <bean id="accessDecisionManager" class="com.flysand.access.MyAccessDecisionManager">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
            </list>
        </constructor-arg>
    </bean>

    <!--资源http配置-->
    <security:http pattern="/abcs/**" create-session="never" entry-point-ref="clientAuthenticationEntryPoint"
                   access-decision-manager-ref="accessDecisionManager">
        <security:anonymous enabled="false"/>
        <security:intercept-url pattern="/abcs/**" access="hasRole('ROLE_USER')"/>
        <security:custom-filter ref="myResourceService" before="PRE_AUTH_FILTER"/>
        <security:access-denied-handler ref="accessDeniedHandler"/>
    </security:http>
</beans>

 在t1.jsp里面写一个ajax请求用来获取access_token

<%--
  Created by IntelliJ IDEA.
  User: jianyi
  Date: 2017年3月31日 0031
  Time: 13:47:16
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta name="_csrf" content="${_csrf.token}">
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
    <script type="text/javascript">
        $(function () {

            var token = $("meta[name='_csrf']").attr("content");
            var header = $("meta[name='_csrf_header']").attr("content");
            $('#ajax').click(function () {
                var url = "user/getUsers";
                url = 'oauth/token';
                $.ajax({
                    type:'post',
                    url:url,
                    dataType:'json',
                    data:'client_id=test&client_secret=123456&grant_type=password&username=user&password=123',
                    //data:'pageIndex=1&pageSize=10',
                    success:function(data){
                        console.log(data)
                    },
                    error:function (data) {
                        console.log("系统异常");
                    }/*,
                     complete:function (data) {
                     console.log(data);
                     }*/

                });

            });
        });
    </script>
</head>
<body>
mytest

<input id="ajax" type="button" value="测试ajax"/>
</body>
</html>

结果:

根据access_token请求保护的资源

完整项目:

https://github.com/symflysand/OauthTest

原文地址:https://www.cnblogs.com/flysand/p/6728879.html