security和oauth2.0的整合

以前已經介紹過security的相關的介紹,如今所須要作的就是security和oauth2.0的整合,在原有的基礎上咱們加上一些相關的代碼;代碼實現以下:php

pom.xml:java

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>urity.demo</groupId>
    <artifactId>security-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <!--如下兩項須要若是不配置,解析themleaft 會有問題-->
        <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.0.5</thymeleaf-layout-dialect.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>io.spring.platform</groupId>
                <artifactId>platform-bom</artifactId>
                <version>Brussels-SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--mybatis與mysql-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.25</version>
        </dependency>
        <!--redis依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--jasypt加解密-->
        <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>1.14</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <!--oauth2.0-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

        <!--session集羣管理-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <!--zipkin-->
       <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency>-->

        <!--eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <!--添加static和templates的依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <!--config-->
        <!--<dependency>-->
        <!--<groupId>org.springframework.cloud</groupId>-->
        <!--<artifactId>spring-cloud-starter-config</artifactId>-->
        <!--</dependency>-->
        <!--<dependency>-->
        <!--<groupId>org.springframework.cloud</groupId>-->
        <!--<artifactId>spring-cloud-starter-bus-amqp</artifactId>-->
        <!--</dependency>-->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

複製代碼

這裏咱們須要注意導入依賴的版本,版本太高可能會存在一些未知的問題:mysql

AuthorizationServerConfiguration核心類:git

package urity.demo.oauth2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import urity.demo.service.RedisAuthenticationCodeServices;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Value("${resource.id:spring-boot-application}")
    private String resourceId;

    @Value("${access_token.validity_period:36000}")
    private int accessTokenValiditySeconds = 36000;

    //認證管理 很重要 若是security版本高可能會出坑哦
    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisAuthenticationCodeServices redisAuthenticationCodeServices;


    //定義令牌端點上的安全約束。
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
        oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");

    }

    //將ClientDetailsServiceConfigurer(從您的回調AuthorizationServerConfigurer)能夠用來在內存或JDBC實現客戶的細節服務來定義的。客戶端的重要屬性是
    //clientId:(必填)客戶端ID。
    //secret:(可信客戶端須要)客戶機密碼(若是有)。
    //scope:客戶受限的範圍。若是範圍未定義或爲空(默認值),客戶端不受範圍限制。
    //authorizedGrantTypes:授予客戶端使用受權的類型。默認值爲空。
    //authorities授予客戶的受權機構(普通的Spring Security權威機構)。
    //客戶端的詳細信息能夠經過直接訪問底層商店(例如,在數據庫表中JdbcClientDetailsService)或經過ClientDetailsManager接口(這兩種實現ClientDetailsService也實現)來更新運行的應用程序。
    //注意:JDBC服務的架構未與庫一塊兒打包(由於在實踐中可能須要使用太多變體)
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //默認值InMemoryTokenStore對於單個服務器是徹底正常的(即,在發生故障的狀況下,低流量和熱備份備份服務器)。大多數項目能夠從這裏開始,也能夠在開發模式下運行,以便輕鬆啓動沒有依賴關係的服務器。
        //這JdbcTokenStore是同一件事的JDBC版本,它將令牌數據存儲在關係數據庫中。若是您能夠在服務器之間共享數據庫,則可使用JDBC版本,若是隻有一個,則擴展同一服務器的實例,或者若是有多個組件,則受權和資源服務器。要使用JdbcTokenStore你須要「spring-jdbc」的類路徑。


        clients.inMemory()
                //client Id
                .withClient("normal-app")
                .authorizedGrantTypes("authorization_code", "implicit")
                .authorities("ROLE_CLIENT")
                .scopes("read","write")
                .resourceIds(resourceId)
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                .and()
                .withClient("trusted-app")
                .authorizedGrantTypes("client_credentials", "password")
                .authorities("ROLE_TRUSTED_CLIENT")
                .scopes("read", "write")
                .resourceIds(resourceId)
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                .secret("secret");




    }

    //AuthorizationEndpoint能夠經過如下方式配置支持的受權類型AuthorizationServerEndpointsConfigurer。默認狀況下,全部受權類型均受支持,除了密碼(有關如何切換它的詳細信息,請參見下文)。如下屬性會影響受權類型:
    //authenticationManager:經過注入密碼受權被打開AuthenticationManager。
    //userDetailsService:若是您注入UserDetailsService或者全局配置(例如a GlobalAuthenticationManagerConfigurer),則刷新令牌受權將包含對用戶詳細信息的檢查,以確保該賬戶仍然活動
    //authorizationCodeServices:定義AuthorizationCodeServices受權代碼受權的受權代碼服務(實例)。
    //implicitGrantService:在批准期間管理狀態。
    //tokenGranter:(TokenGranter徹底控制授予和忽略上述其餘屬性)
    //在XML授予類型中包含做爲子元素authorization-server。

    /** * /oauth/authorize您能夠從該請求中獲取全部數據, * 而後根據須要進行渲染, * 而後全部用戶須要執行的操做都是回覆有關批准或拒絕受權的信息。 * 請求參數直接傳遞給您UserApprovalHandler, * AuthorizationEndpoint因此您能夠隨便解釋數據 * * @param endpoints * @throws Exception */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(this.authenticationManager);
        endpoints.accessTokenConverter(accessTokenConverter());//jwt
        endpoints.tokenStore(tokenStore());

        //受權碼存儲
        endpoints.authorizationCodeServices(redisAuthenticationCodeServices);

    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {

        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            /** * 重寫加強token的方法 * 自定義返回相應的信息 * */

            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {

                String userName = authentication.getUserAuthentication().getName();
                // 與登陸時候放進去的UserDetail實現類一直查看link{SecurityConfiguration}
                User user = (User) authentication.getUserAuthentication().getPrincipal();
                /** 自定義一些token屬性 ***/
                final Map<String, Object> additionalInformation = new HashMap<>();
                additionalInformation.put("userName", userName);
                additionalInformation.put("roles", user.getAuthorities());
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                return enhancedToken;
            }

        };
        // 測試用,資源服務使用相同的字符達到一個對稱加密的效果,生產時候使用RSA非對稱加密方式
        accessTokenConverter.setSigningKey("123");
        return accessTokenConverter;

    }

    @Bean
    public TokenStore tokenStore() {

        TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());

        return tokenStore;
    }

}

複製代碼

RedisAuthenticationCodeServices:github

咱們把受權碼存在了redis中:web

package urity.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.security.oauth2.common.util.SerializationUtils;


//自定義爲使用redis存儲受權碼
@Service
@Slf4j
public class RedisAuthenticationCodeServices extends RandomValueAuthorizationCodeServices {

    private static final String AUTH_CODE_KEY = "my_code";

    private RedisConnectionFactory connectionFactory;

    public RedisAuthenticationCodeServices(RedisConnectionFactory connectionFactory) {
        Assert.notNull(connectionFactory, "RedisConnectionFactory required");
        this.connectionFactory = connectionFactory;
    }

    private RedisConnection getConnection() {

        return connectionFactory.getConnection();
    }

    //redis存儲
    @Override
    protected void store(String code, OAuth2Authentication authentication) {

        RedisConnection conn = getConnection();

        try {


            conn.hSet(AUTH_CODE_KEY.getBytes("utf-8"), code.getBytes("utf-8"),

                    SerializationUtils.serialize(authentication)
            );
        } catch (Exception e) {

            conn.close();
        }

    }

    @Override
    protected OAuth2Authentication remove(String code) {
        RedisConnection conn = getConnection();
        try {


            OAuth2Authentication authentication = null;

            try {
                authentication = SerializationUtils
                        .deserialize(conn.hGet(AUTH_CODE_KEY.getBytes("utf-8"),
                                code.getBytes("utf-8")));


            } catch (Exception e) {
                e.printStackTrace();


            }


            if (authentication != null) {
                conn.hDel(AUTH_CODE_KEY.getBytes("utf-8"),
                        code.getBytes("utf-8"));
            }

            return authentication;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.close();
        }
        return null;
    }
}
複製代碼

ResourceController:redis

package urity.demo.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/*** * 受保護的資源服務 * @author leftso * */
@RestController
@RequestMapping("/resources")
public class ResourceController {
	/** * 須要用戶角色權限 * @return */
    @PreAuthorize("hasRole('ROLE_USER')")
    @RequestMapping(value="user", method=RequestMethod.GET)
    public String helloUser() {
        return "hello user";
    }
    /** * 須要管理角色權限 * * @return */
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(value="admin", method=RequestMethod.GET)
    public String helloAdmin() {
        return "hello admin";
    }
    /** * 須要客戶端權限 * * @return */
    @PreAuthorize("hasRole('ROLE_CLIENT')")
    @RequestMapping(value="client", method=RequestMethod.GET)
    public String helloClient() {
        return "hello user authenticated by normal client";
    }
    /** * 須要受信任的客戶端權限 * * @return */
    @PreAuthorize("hasRole('ROLE_TRUSTED_CLIENT')")
	@RequestMapping(value="trusted_client", method=RequestMethod.GET)
    public String helloTrustedClient() {
        return "hello user authenticated by trusted client";
    }

	@RequestMapping(value="principal", method=RequestMethod.GET)
    public Object getPrincipal() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return principal;
    }

	@RequestMapping(value="roles", method=RequestMethod.GET)
    public Object getRoles() {
        return SecurityContextHolder.getContext().getAuthentication().getAuthorities();
    }

}

複製代碼

application.xml:spring

server:
  port: 8787
spring:
  redis:
    host: 127.0.0.1
    port: 6379
#    password: redis
    database: 0
  datasource:
      url: jdbc:mysql://localhost:3306/test
      username: ***
      password: ***
      driver-class-name: com.mysql.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      initialSize: 5
      minIdle: 5
      maxActive: 30
      maxWait: 10000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMills: 300000
  session:
    store-type: none
other:
  security:
    oauth2:
      signKey: oauth

複製代碼
相關文章
相關標籤/搜索