草庐IT

java - Spring OAuth2 为每个请求生成访问 token 到 token 端点

coder 2024-03-05 原文

是否可以使用每个请求的 client_credentials 或密码授予类型生成多个有效的访问 token ?

使用上述授权类型生成 token 只会在每个请求的当前 token 到期时才提供新 token 。

我可以使用密码授予类型来生成刷新 token ,然后生成多个访问 token ,但这样做会使以前的任何访问 token 失效。

知道如何更改以允许针对/oauth/token 端点的每个请求生成访问 token 并确保任何以前的 token 不会失效吗?

下面是我的 oauth 服务器的 XML 配置。

<!-- oauth2 config start-->
  <sec:http pattern="/test/oauth/token" create-session="never"
              authentication-manager-ref="authenticationManager" > 
        <sec:intercept-url pattern="/test/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <sec:anonymous enabled="false" />
        <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
        <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> 
        <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> 
    </sec:http>


    <bean id="clientCredentialsTokenEndpointFilter"
          class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="authenticationManager" />
    </bean>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider user-service-ref="clientDetailsUserService" />
    </sec:authentication-manager>

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

    <bean id="clientDetails" class="org.security.oauth2.ClientDetailsServiceImpl"></bean>

    <bean id="clientAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="springsec/client" />
        <property name="typeName" value="Basic" />
    </bean>

    <bean id="oauthAccessDeniedHandler"
          class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>

    <oauth:authorization-server
        client-details-service-ref="clientDetails" token-services-ref="tokenServices">
        <oauth:authorization-code />
        <oauth:implicit/>
        <oauth:refresh-token/>
        <oauth:client-credentials />
        <oauth:password authentication-manager-ref="userAuthenticationManager"/>
    </oauth:authorization-server>

    <sec:authentication-manager id="userAuthenticationManager">
        <sec:authentication-provider  ref="customUserAuthenticationProvider">
        </sec:authentication-provider>
    </sec:authentication-manager>

    <bean id="customUserAuthenticationProvider"
          class="org.security.oauth2.CustomUserAuthenticationProvider">
    </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="300"></property>
        <property name="clientDetailsService" ref="clientDetails" />
    </bean>

    <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="password"/>
    </bean>
    <bean id="oauthAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    </bean>

最佳答案

21/11/2014 更新

当我仔细检查时,我发现 InMemoryTokenStore使用 OAuth2Authentication的哈希字符串作为多个 key Map .当我使用相同的用户名、client_id、范围时......我得到了相同的 key .所以这可能会导致一些问题。所以我认为旧的方式已被弃用。以下是我为避免该问题所做的工作。

创建另一个 AuthenticationKeyGenerator可以计算唯一键,称为UniqueAuthenticationKeyGenerator

/*
 * Copyright 2006-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.
 */

/**
 * Basic key generator taking into account the client id, scope, resource ids and username (principal name) if they
 * exist.
 * 
 * @author Dave Syer
 * @author thanh
 */
public class UniqueAuthenticationKeyGenerator implements AuthenticationKeyGenerator {

    private static final String CLIENT_ID = "client_id";

    private static final String SCOPE = "scope";

    private static final String USERNAME = "username";

    private static final String UUID_KEY = "uuid";

    public String extractKey(OAuth2Authentication authentication) {
        Map<String, String> values = new LinkedHashMap<String, String>();
        OAuth2Request authorizationRequest = authentication.getOAuth2Request();
        if (!authentication.isClientOnly()) {
            values.put(USERNAME, authentication.getName());
        }
        values.put(CLIENT_ID, authorizationRequest.getClientId());
        if (authorizationRequest.getScope() != null) {
            values.put(SCOPE, OAuth2Utils.formatParameterList(authorizationRequest.getScope()));
        }
        Map<String, Serializable> extentions = authorizationRequest.getExtensions();
        String uuid = null;
        if (extentions == null) {
            extentions = new HashMap<String, Serializable>(1);
            uuid = UUID.randomUUID().toString();
            extentions.put(UUID_KEY, uuid);
        } else {
            uuid = (String) extentions.get(UUID_KEY);
            if (uuid == null) {
                uuid = UUID.randomUUID().toString();
                extentions.put(UUID_KEY, uuid);
            }
        }
        values.put(UUID_KEY, uuid);

        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
        }

        try {
            byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
            return String.format("%032x", new BigInteger(1, bytes));
        }
        catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
        }
    }
}

最后,将它们连接起来
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
    <constructor-arg ref="jdbcTemplate" />
    <property name="authenticationKeyGenerator">
        <bean class="your.package.UniqueAuthenticationKeyGenerator" />
    </property>
</bean>

以下方式可能会导致一些问题,请参阅更新的答案!!!

您正在使用 DefaultTokenServices。试试这个代码并确保重新定义你的 `tokenServices`

包 com.thanh.backend.oauth2.core;

导入 java.util.Date;
导入 java.util.UUID;

导入 org.springframework.security.core.AuthenticationException;
导入 org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
导入 org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
导入 org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
导入 org.springframework.security.oauth2.common.OAuth2AccessToken;
导入 org.springframework.security.oauth2.common.OAuth2RefreshToken;
导入 org.springframework.security.oauth2.provider.OAuth2Authentication;
导入 org.springframework.security.oauth2.provider.token.DefaultTokenServices;
导入 org.springframework.security.oauth2.provider.token.TokenEnhancer;
导入 org.springframework.security.oauth2.provider.token.TokenStore;

/**
* @author 谢谢
*/
公共(public)类 SimpleTokenService 扩展 DefaultTokenServices {

私有(private) TokenStore tokenStore;

私有(private) TokenEnhancer 访问 token 增强器;

@覆盖
公共(public) OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) 抛出 AuthenticationException {

OAuth2RefreshToken refreshToken = createRefreshToken(authentication);;
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
tokenStore.storeRefreshToken(refreshToken, authentication);
返回访问 token ;
}

私有(private) OAuth2AccessToken createAccessToken(OAuth2Authentication 认证,OAuth2RefreshToken refreshToken) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
intvaliditySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
如果(validitySeconds > 0){
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
}
token.setRefreshToken(refreshToken);
token.setScope(authentication.getOAuth2Request().getScope());

返回 accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token ;
}

private ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
如果 (!isSupportRefreshToken(authentication.getOAuth2Request())) {
返回空;
}
intvaliditySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(UUID.randomUUID().toString(),
新日期(System.currentTimeMillis() + (validitySeconds * 1000L)));
返回刷新 token ;
}

@覆盖
public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
super.setTokenEnhancer(accessTokenEnhancer);
this.accessTokenEnhancer = accessTokenEnhancer;
}

@覆盖
public void setTokenStore(TokenStore tokenStore) {
super.setTokenStore(tokenStore);
this.tokenStore = tokenStore;
}
}

关于java - Spring OAuth2 为每个请求生成访问 token 到 token 端点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27020702/

有关java - Spring OAuth2 为每个请求生成访问 token 到 token 端点的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  3. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  6. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  7. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. ruby-on-rails - Rails 中的 NoMethodError::MailersController#preview undefined method `activation_token=' for nil:NilClass - 2

    似乎无法为此找到有效的答案。我正在阅读Rails教程的第10章第10.1.2节,但似乎无法使邮件程序预览正常工作。我发现处理错误的所有答案都与教程的不同部分相关,我假设我犯的错误正盯着我的脸。我已经完成并将教程中的代码复制/粘贴到相关文件中,但到目前为止,我还看不出我输入的内容与教程中的内容有什么区别。到目前为止,建议是在函数定义中添加或删除参数user,但这并没有解决问题。触发错误的url是http://localhost:3000/rails/mailers/user_mailer/account_activation.http://localhost:3000/rails/mai

  10. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

随机推荐