Spring 5.0+Spring Boot+security+spring cloud oauth2+Redis整合詳情,記錄那些遇到的一些坑__Spring5.0

來源:互聯網
上載者:User
1、使用的技術以及版本號碼 JDK8.0 Spring 5.0 oauth2.0 redis2.0
2、項目採用MAVEN管理。 pom檔案中加入: < dependency > < groupId > org.springframework.cloud </ groupId > < artifactId > spring-cloud-starter-security </ artifactId > </ dependency >
< dependency > < groupId > org.springframework.cloud </ groupId > < artifactId > spring-cloud-starter-oauth2 </ artifactId > </ dependency >
< dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-data-redis </ artifactId > </ dependency >
< dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-data-jpa </ artifactId > </ dependency >
< dependency > < groupId > org.thymeleaf </ groupId > < artifactId > thymeleaf-spring5 </ artifactId > </ dependency >

3、資料的儲存 現採用Redis來進行儲存。所以決定token儲存在redis中,client資訊和code資訊儲存在資料庫表中。 OAUTH2.0涉及到的資料庫表有詳細說明: http://andaily.com/spring-oauth-server/db_table_description.html


4、 Spring5.0 新版本中,有很多的方法和類發生了改變,導致現在網上大多數的認證代碼不能使用,運行報錯。經過查詢與研究。終於運行起來了。需要注意的有: RedisConnectionFactory儲存token的時候會出現錯誤,這個時候需要自訂MyRedisTokenStore類,實現TokenStore。MyRedisTokenStore和RedisTokenStore代碼差不多一樣,只是把所有conn.set(…)都換成conn..stringCommands().set(…), PasswordEncoder密碼驗證clientId的時候會報錯,因為5.0新特性中需要在密碼前方需要加上{Xxx}來判別。所以需要自訂一個類,重新BCryptPasswordEncoder的match方法。

總之坑很多,我們只有一步一個坑慢慢的前行去填坑。

5、

相關學習資料 oauth2.0+security相關資料: https://www.cnblogs.com/sheng-jie/p/6564520.html https://developers.douban.com/wiki/?title=oauth2 http://andaily.com/spring-oauth-server/db_table_description.html http://blog.csdn.net/greatneyo/article/details/77979848 http://www.tianshouzhi.com/api/tutorials/spring_security_4/264 https://www.cnblogs.com/xingxueliao/p/5911292.html https://docs.spring.io/spring-security/site/docs/3.2.0.RC2/apidocs/org/springframework/security/config/annotation/web/builders/HttpSecurity.html
出現bug,需要尋找解決辦法的時候,如果在百度裡面沒找到相關的資料,可以在 https://stackoverflow.com/ 中去尋找。
reids2.0相關資料: https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#new-in-2.0.0 。


6、核心代碼:

package com.hbasesoft.vcc.sgp.ability.oauth.server.config;import com.hbasesoft.framework.db.core.config.DbParam;import com.hbasesoft.framework.db.core.utils.DataSourceUtil;import com.hbasesoft.vcc.sgp.ability.oauth.server.security.MyBCryptPasswordEncoder;import com.hbasesoft.vcc.sgp.ability.oauth.server.security.MyRedisTokenStore;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;import org.springframework.security.oauth2.provider.ClientDetailsService;import org.springframework.security.oauth2.provider.approval.ApprovalStore;import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.sql.DataSource;/** * @Author: fb * @Description  client資訊通過jdbc去查詢資料庫驗證,(注釋部分是通過Redis方法去驗證) * @Date: Create in 14:54 2018/2/1 * @Modified By */@Configuration@EnableAuthorizationServerpublic class OAuthServerConfig extends AuthorizationServerConfigurerAdapter {    @Autowired    private AuthenticationManager authenticationManager;    //本系統獲得dataSource的方式    private DataSource dataSource = DataSourceUtil.registDataSource("master", new DbParam("master"));    @Autowired    private RedisConnectionFactory connectionFactory;    @Bean    public PasswordEncoder passwordEncoder() {        return new MyBCryptPasswordEncoder();    }   /*    @Autowired    private ClientDetailsService clientDetailsService;   */    @Bean    public MyRedisTokenStore tokenStore() {        return new MyRedisTokenStore(connectionFactory);    }    @Bean    public AuthorizationCodeServices authorizationCodeServices() {        return new JdbcAuthorizationCodeServices(dataSource);    }    @Bean    public ClientDetailsService clientDetailsService() {        return new JdbcClientDetailsService(dataSource);    }    @Bean    public ApprovalStore approvalStore() {        TokenApprovalStore store = new TokenApprovalStore();        store.setTokenStore(tokenStore());        return store;    }    /*    @Bean    public UserApprovalHandler userApprovalHandler() throws Exception {        MyUserApprovalHandler handler = new MyUserApprovalHandler();        handler.setApprovalStore(approvalStore());        handler.setClientDetailsService(clientDetailsService);        handler.setRequestFactory(new DefaultOAuth2RequestFactory(                clientDetailsService));        handler.setUseApprovalStore(true);        return handler;    }    */    @Override    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {//         //用戶端資訊通過Redis去取得驗證//        final RedisClientDetailsServiceBuilder builder = new RedisClientDetailsServiceBuilder();//        clients.setBuilder(builder);        //通過JDBC去查詢資料庫oauth_client_details表驗證clientId資訊        clients.jdbc(this.dataSource).clients(this.clientDetailsService());    }    @Override    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {        endpoints.authenticationManager(authenticationManager)                .tokenStore(this.tokenStore())                // .userDetailsService(userDetailsService)                // .userApprovalHandler(userApprovalHandler())                .authorizationCodeServices(this.authorizationCodeServices());    }    @Override    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");    }}

以上代碼的dataSource是我們架構系統封裝好的,如果你們dataSource沒有封裝好,則直接用

  @Autowired    private DataSource dataSource;

MyBCryptPasswordEncoder類是我自訂的一個類,用來重新match方法。
package com.hbasesoft.vcc.sgp.ability.oauth.server.security;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.factory.PasswordEncoderFactories;import org.springframework.security.crypto.password.PasswordEncoder;/** * <Description> <br> * * @author fb<br> * @version 1.0<br> * @taskId <br> * @CreateDate Create in 13:50 2018/2/12 * @see com.hbasesoft.vcc.sgp.ability.oauth.server.security <br> * @since V1.0<br> */public class MyBCryptPasswordEncoder extends BCryptPasswordEncoder {    @Override    public boolean matches(CharSequence rawPassword, String encodedPassword) {        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();        String presentedPassword =passwordEncoder.encode(encodedPassword);        return passwordEncoder.matches(rawPassword, presentedPassword);    }}

上面的類中,client資訊採用的是儲存在資料庫中,而token資訊,則採用redis來儲存,redis用RedisTokenStore來實現,但是由於在Spring的5.0中會出現如下錯誤:

我將我的spring boot項目版本升到2.0.0.M7後,整合了spring security oauth2(預設版本),redis(預設版本),並且用redis來儲存token。項目正常啟動後,請求token時報錯

nested exception is java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.set([B[B)V

看到報錯資訊,我的第一反應是版本衝突,但是我的依賴的都是spring-boot-starter-parent中的預設版本。 pring-data-redis 2.0版本中set(String,String)被棄用了。然後我按照網頁中的決解方法“spring-date-redis”改為2.0.1.RELEASE和”jedis“為2.9.0(顯式聲明),結果還是報一樣的錯。用了一個極端的方式解決的。spring-data-redis 2.0版本中set(String,String)被棄用了,要使用RedisConnection.stringCommands().set(…),所有我自訂一個RedisTokenStore,代碼和RedisTokenStore一樣,只是把所有conn.set(…)都換成conn..stringCommands().set(…),測試後方法可行。

MyRedisTokenStore.java類代碼如下:

package com.hbasesoft.vcc.sgp.ability.oauth.server.security;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Date;import java.util.Iterator;import java.util.List;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;import org.springframework.security.oauth2.common.OAuth2AccessToken;import org.springframework.security.oauth2.common.OAuth2RefreshToken;import org.springframework.security.oauth2.provider.OAuth2Authentication;import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;import org.springframework.stereotype.Component;import java.util.*;/** * <Description> 重寫tokenStore .因為最新版中RedisTokenStore的set已經被棄用了, * 所以我就只能自訂一個,代碼和RedisTokenStore一樣, * 只是把所有conn.set(…)都換成conn..stringCommands().set(…), * <br> * * @author fb<br> * @version 1.0<br> * @taskId <br> * @CreateDate Create in 10:59 2018/2/13 * @see com.hbasesoft.vcc.sgp.ability.oauth.server.security <br> * @since V1.0<br> */@Componentpublic class MyRedisTokenStore implements TokenStore {    private static final String ACCESS = "access:";    private static final String AUTH_TO_ACCESS = "auth_to_access:";    private static final String AUTH = "auth:";    private static final String REFRESH_AUTH = "refresh_auth:";    private static final String ACCESS_TO_REFRESH = "access_to_refresh:";    private static final String REFRESH = "refresh:";    private static final String REFRESH_TO_ACCESS = "refresh_to_access:";    private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";    private static final String UNAME_TO_ACCESS = "uname_to_access:";    private final RedisConnectionFactory connectionFactory;    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();    private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();    private String prefix = "";    public MyRedisTokenStore(RedisConnectionFactory connectionFactory) {        this.connectionFactory = connectionFactory;    }    public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {        this.authenticationKeyGenerator = authenticationKeyGenerator;    }    public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {        this.serializationStrategy = serializationStrategy;    }    public void setPrefix(String prefix) {        this.prefix = prefix;    }    private RedisConnection getConnection() {        return this.connectionFactory.getConnection();    }    private byte[] serialize(Object object) {        return this.serializationStrategy.serialize(object);    }    private byte[] serializeKey(String object) {        return this.serialize(this.prefix + object);    }    private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {        return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);    }    private OAuth2Authentication deserializeAuthentication(byte[] bytes) {        return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);    }    private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {        return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);    }    private byte[] serialize(String string) {        return this.serializationStrategy.serialize(string);    }    private String deserializeString(byte[] bytes) {        return this.serializationStrategy.deseri
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.