OAuth2.0入門(二)—— spring-security-oauth2入門例子JDBC存儲(超詳細版)上篇

前言:本文是OAuth2.0實踐篇,閱讀之前需要先掌握OAuth2.0基本原理,原理介紹見:OAuth2.0入門(一)—— 基本概念詳解和圖文並茂講解四種授權類型     

       本章將採用微服務架構方式,將OAuth2-Demo拆分成三個模塊:oauth2-authentication-server(作爲授權認證中心)、oauth2-resource-server(作爲資源服務器)、oauth-client(作爲第三方應用,模擬如何獲取Token訪問資源)。

一、項目總結構

其中oauth2-demo是其他模塊的Parent模塊,定義了一些通用的Jar包。完整的pom文件如下:

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.scb</groupId>
    <artifactId>oauth2-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>oauth2-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.11</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

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

    <modules>
        <module>oauth2-authentication-server</module>
        <module>oauth2-resource-server</module>
        <module>oauth-client</module>
    </modules>

</project>

二、oauth2-authentication-server 模塊

oauth2-authentication-server 模塊是作爲全局的授權認證中心,pom文件如下:

<?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>
    <parent>
        <groupId>com.scb</groupId>
        <artifactId>oauth2-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>oauth2-authentication-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>oauth2-authentication-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- 阿里系的Druid依賴包 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>
        <!-- Druid 依賴 log4j包 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

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

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

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

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

這裏,除了導入spring-boot-starter-security和spring-security-oauth2認證框架外,還需要使用H2內存數據庫來存儲用戶和角色信息及OAuth2的表。

先來看看application.yml文件:

spring:
  h2:
    console:
      path: /h2-console
      enabled: true
      settings:
        web-allow-others: true
  jpa:
    generate-ddl: false
    show-sql: true
    hibernate:
      ddl-auto: none
  datasource:
    platform: h2
    schema: classpath:schema.sql
    data: classpath:data.sql
    url: jdbc:h2:~/auth;AUTO_SERVER=TRUE
    username: sa
    password:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      min-idle: 2
      initial-size: 5
      max-active: 10
      max-wait: 5000
      validation-query: select 1
  resources:
    static-locations: classpath:/templates/,classpath:/static/
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML5
    servlet:
      content-type: text/html
    cache: false
server:
  port: 8080
logging:
  pattern:
    level: debug

在yml文件中,我們定義了datasource爲H2,並指定了schema、data文件,這樣在項目運行時會執行相應的sql。其中schema.sql文件如下:

/* 1、存放用戶認證信息及權限 */
drop table if exists authority;
CREATE TABLE authority (
  id  integer,
  authority varchar(255),
  primary key (id)
);

drop table if exists credentials;
CREATE TABLE credentials (
  id  integer,
  enabled boolean not null,
  name varchar(255) not null,
  password varchar(255) not null,
  version integer,
  primary key (id)
);

drop table if exists credentials_authorities;
CREATE TABLE credentials_authorities (
  credentials_id bigint not null,
  authorities_id bigint not null
);

/* 2、oauth2官方建表語句 */
drop table if exists oauth_client_token;
create table oauth_client_token (
  token_id VARCHAR(255),
  token LONGBLOB,
  authentication_id VARCHAR(255),
  user_name VARCHAR(255),
  client_id VARCHAR(255)
);

drop table if exists oauth_client_details;
CREATE TABLE oauth_client_details (
  client_id varchar(255) NOT NULL,
  resource_ids varchar(255) DEFAULT NULL,
  client_secret varchar(255) DEFAULT NULL,
  scope varchar(255) DEFAULT NULL,
  authorized_grant_types varchar(255) DEFAULT NULL,
  web_server_redirect_uri varchar(255) DEFAULT NULL,
  authorities varchar(255) DEFAULT NULL,
  access_token_validity integer(11) DEFAULT NULL,
  refresh_token_validity integer(11) DEFAULT NULL,
  additional_information varchar(255) DEFAULT NULL,
  autoapprove varchar(255) DEFAULT NULL
);

drop table if exists oauth_access_token;
create table `oauth_access_token` (
  token_id VARCHAR(255),
  token LONGBLOB,
  authentication_id VARCHAR(255),
  user_name VARCHAR(255),
  client_id VARCHAR(255),
  authentication LONGBLOB,
  refresh_token VARCHAR(255)
);

drop table if exists oauth_refresh_token;
create table `oauth_refresh_token`(
  token_id VARCHAR(255),
  token LONGBLOB,
  authentication LONGBLOB
);

drop table if exists oauth_code;
create table oauth_code (
  code VARCHAR(255), 
  authentication BLOB
);

drop table if exists oauth_approvals;
create table oauth_approvals (
    userId VARCHAR(255),
    clientId VARCHAR(255),
    scope VARCHAR(255),
    status VARCHAR(10),
    expiresAt DATETIME,
    lastModifiedAt DATETIME
);

這裏分爲兩部分,第一部分是自定義的用於存放用戶憑證及授權的表。第二部分是官方建表語句:spring-security-oauth schema.sql,各個數據表說明如下:

  • oauth_client_details:存放client信息,主要操作類爲JdbcClientDetailsService
  • oauth_client_token:存放client的Token信息。即通過client_credentials授權方式獲得的Token。主要操作類爲JdbcClientTokenServices
  • oauth_access_token:存放access token等,主要操作類爲JdbcTokenStore
  • oauth_refresh_token:跟oath_access_token表類似,當client的grant type支持refresh token時纔有記錄。主要操作類爲JdbcTokenStore
  • oauth_code:存放授權碼(Authorization Code),即當client的grant type支持authorization_code時纔有記錄。主要操作類爲JdbcAuthorizationCodeServices
  • oauth_approvals:存放用戶授權client的信息,即當client的grant type支持authorization_code時纔有記錄。主要操作類爲JdbcApprovalStore

字段及表詳細說明如下,圖片來源:https://blog.csdn.net/qq_34997906/article/details/89609297

oauth2 table

再來看一下data.sql文件,該文件主要是創建一些初始數據。

INSERT INTO authority  VALUES(1,'ROLE_OAUTH_ADMIN');
INSERT INTO authority VALUES(2,'ROLE_RESOURCE_ADMIN');
INSERT INTO authority VALUES(3,'ROLE_PRODUCT_ADMIN');

/* password ==> password */
INSERT INTO credentials VALUES(1,true,'oauth_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials VALUES(2,true,'resource_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials  VALUES(3,true,'product_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials_authorities VALUES (1,1);
INSERT INTO credentials_authorities VALUES (2,2);
INSERT INTO credentials_authorities VALUES (3,3);

/* password ==> password */
INSERT INTO oauth_client_details VALUES('curl_client','product_api', '$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO', 'read,write', 'client_credentials', 'http://localhost:7001/oauth2/accessToken', 'ROLE_PRODUCT_ADMIN', 7200, 0, null, 'true');
INSERT INTO oauth_client_details VALUES ('client_code','product_api','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','read,write','authorization_code,refresh_token','http://localhost:7001/oauth2/code','ROLE_PRODUCT_ADMIN',7200,72000,null,'true');
INSERT INTO oauth_client_details VALUES ('client_implicit',	'product_api'	,'$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO',	'read,write'	,'implicit',	'http://localhost:7001/oauth2/accessToken','ROLE_PRODUCT_ADMIN',7200,72000,null,'true');

因爲項目使用了BCryptPasswordEncoder加密器,所以數據庫的密碼統一加密存儲。

用戶名 密碼 權限
oauth_admin password ROLE_OAUTH_ADMIN
resource_admin password ROLE_RESOURCE_ADMIN
product_admin password ROLE_PRODUCT_ADMIN

接下來是Spring Security的配置部分。

1、Entity層

package com.scb.oauth2authenticationserver.entity;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import java.io.Serializable;

@Data
@Entity
@Table(name = "authority")
public class Authority implements GrantedAuthority, Serializable {
    private static final long serialVersionUID = -4737795841774495818L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "AUTHORITY")
    private String authority;
}
package com.scb.oauth2authenticationserver.entity;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Data
@Entity
@Table(name = "credentials")
public class Credentials implements Serializable {
    private static final long serialVersionUID = -1408491858754963752L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "enabled")
    private boolean enabled;

    @Column(name = "name",nullable = false)
    private String name;

    @Column(name = "password",nullable = false)
    private String password;

    @Version
    @Column(name = "version",nullable = false)
    private Integer version;

    @ManyToMany(fetch = FetchType.EAGER)
    private List<Authority> authorities;
}

Entity層是實體層,映射數據表的字段。其中@Version註解是JPA實現的樂觀鎖機制。

2、Repository層

package com.scb.oauth2authenticationserver.repository;

import com.scb.oauth2authenticationserver.entity.Credentials;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CredentialsRepository extends JpaRepository<Credentials,Long> {
    Credentials findByName(String name);
}

3、配置UserDetailsService

package com.scb.oauth2authenticationserver.service;

import com.scb.oauth2authenticationserver.entity.Credentials;
import com.scb.oauth2authenticationserver.repository.CredentialsRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class JdbcUserDetailsService implements UserDetailsService {
    @Autowired
    private CredentialsRepository credentialsRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Credentials credentials = credentialsRepository.findByName(username);
        if (credentials == null){
            throw new UsernameNotFoundException("User "+username+" cannot be found");
        }
        User user = new User(credentials.getName(),credentials.getPassword(),credentials.isEnabled(),true,true,true,credentials.getAuthorities());
        return user;
    }
}
UserDetailsService用於做Spring Security登錄認證。關於Spring Security認證流程見:Spring Security 認證流程詳解

4、SpringSecurityConfig

package com.scb.oauth2authenticationserver.config;

import com.scb.oauth2authenticationserver.service.JdbcUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return new JdbcUserDetailsService();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**","/js/**","/fonts/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
//                .addFilter()
//                .antMatcher("oauth/authorize")
                .authorizeRequests()
                .antMatchers("/login","/logout.do").permitAll()
                .antMatchers("/**").authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login.do")
                .usernameParameter("username")
                .passwordParameter("password")
                .loginPage("/login")
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout.do"))
                .and()
                .userDetailsService(userDetailsServiceBean());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceBean())
        .passwordEncoder(passwordEncoder());
    }

}

SpringSecurityConfig模塊一共有3個configure,分別是認證相關的AuthenticationManagerBuilder和web相關的WebSecurity、HttpSecurity。

  • AuthenticationManagerBuilder:用來配置全局的認證相關的信息,其實就是AuthenticationProvider和UserDetailsService。
  • WebSecurity:用來配置全局請求忽略規則(比如靜態文件)、全局HttpFirewall、是否debug、全局SecurityFilterChain等。
  • HttpSecurity:用來配置具體的權限控制規則

5、Controller層

package com.scb.oauth2authenticationserver.controller;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class LoginController {
    @Autowired
    private JdbcClientDetailsService clientDetailsService;

    @Autowired
    private ApprovalStore approvalStore;

    @Autowired
    private TokenStore tokenStore;

    @InitBinder
    protected void init(HttpServletRequest request, ServletRequestDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    @RequestMapping("/login")
    public String loginPage() {
        tokenStore.findTokensByClientId("client_code").stream().forEach(accessToken -> log.info(accessToken.toString()));
        return "login";
    }


    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "redirect:/login?logout";
    }

    @RequestMapping("/")
    public ModelAndView root(Map<String, Object> model) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        log.info(authentication.getName());
        List<ClientDetails> clientDetails = clientDetailsService.listClientDetails();
        clientDetails.stream().forEach(clientDetails1 -> log.info(clientDetails1.toString()));
        List<Approval> approvals = clientDetails.stream()
                .map(clientDetails1 -> approvalStore.getApprovals(authentication.getName(), clientDetails1.getClientId()))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
        approvals.stream().forEach(approval -> log.info(approval.toString()));
        model.put("approvals", approvals);
        model.put("clientDetails", clientDetails);
        return new ModelAndView("index", model);
    }

    @RequestMapping(value = "/approval/revoke", method = RequestMethod.POST)
    public String revokeApproval(@ModelAttribute Approval approval) {
        log.info(approval.toString());
        Boolean bool = approvalStore.revokeApprovals(Arrays.asList(approval));
        log.info(bool.toString());
        tokenStore.findTokensByClientIdAndUserName(approval.getClientId(), approval.getUserId())
                .forEach(tokenStore::removeAccessToken);
        return "redirect:/";
    }
}

LoginController定義瞭如下四個API:

  • /login:首先,通過TokenStore把client_id爲client_code的access token打印出來(只是試試TokenStore的功能 :) ),然後返回用戶的登錄界面(“login”)。
  • /logout:從SecurityContext取用戶的認證信息,若爲空則直接logout,否則重定向到“/login?logout”界面(Spring默認的退出界面)。
  • /:根目錄,通過ClientDetailsService查找所有的Client信息,再通過ApprovalStore查找當前登錄用戶所授權的所有的Client的Approval信息,最後封裝進model裏面,供View解析渲染。
  • /approval/revoke:該API通過ApprovalStore刪除一個Approval信息,然後通過TokenStore刪除通過該Approval獲得的Token,最後重定向到根目錄。

@InitBinder註解用於SpringMVC表單類型轉換(比如這裏對日期格式做格式化),具體轉換在editor層,代碼就不列出來了。有關@InitBinder註解的更多知識見:SpringMVC註解@initbinder解決類型轉換問題

重頭戲:OAuth2配置部分

package com.scb.oauth2authenticationserver.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
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.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
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.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import lombok.extern.slf4j.Slf4j;

/*
Authorization Server Config
 */
@Slf4j
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    @Qualifier("jdbcUserDetailsService")
    private UserDetailsService userDetailsService;

//    @Autowired
//    private AuthenticationManager authenticationManager;

    public AuthServerConfig() {
        super();
    }

    /*
    oauth_access_token Table
     */
    @Bean
    public TokenStore tokenStore() {
        JdbcTokenStore tokenStore = new JdbcTokenStore(dataSource);
        log.info("Create TokenStore :: " + tokenStore);
        return tokenStore;
    }

    /*
    oauth_client_details Table
    用於配置client信息
     */
    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        log.info("Create ClientDetailsService :: " + clientDetailsService);
        return clientDetailsService;
    }

    /*
    ApprovalStore:用於保存、檢索user approval
    oauth_approvals Table
     */
    @Bean
    public ApprovalStore approvalStore() {
        JdbcApprovalStore approvalStore = new JdbcApprovalStore(dataSource);
        log.info("Create ApprovalStore :: " + approvalStore);
        return approvalStore;
    }

    /*
    oauth_code Table
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        JdbcAuthorizationCodeServices authorizationCodeServices = new JdbcAuthorizationCodeServices(dataSource);
        log.info("Create AuthorizationCodeServices :: " + authorizationCodeServices);
        return authorizationCodeServices;
    }

    /*
    AuthorizationServerSecurityConfigurer:用來配置令牌端點(Token Endpoint)的安全約束
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()");
    }

    /*
    ClientDetailsServiceConfigurer:用來配置客戶端詳情服務
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

    /*
    AuthorizationServerEndpointsConfigurer:來配置授權(authorization)以及令牌(token)的訪問端點和令牌服務(token services)
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.approvalStore(approvalStore())
                .userDetailsService(userDetailsService)
                //.authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices())
                .tokenStore(tokenStore());
    }
}
授權服務器配置:使用 @EnableAuthorizationServer 來配置授權服務機制,並繼承 AuthorizationServerConfigurerAdapter 該類重寫 configure 方法定義授權服務器策略。

TokenStore總共有四種:

  • InMemoryTokenStore:將Token信息存入內存中,是OAuth2的默認實現方式。
  • JdbcTokenStore:將Token信息存入數據庫
  • JwtTokenStore:將相關信息數據存入JWT中,實現無狀態。需要引入 spring-security-jwt 庫。通過JwtAccessTokenConverter進行編碼及解碼,同時需要添加jwtSigningKey,以此生成祕鑰、進行簽名。
  • RedisTokenStore:將Token信息存入Redis中

下面在列出OAuth2的一些默認端點:

  • /oauth/authorize:授權端點,用於grant_type爲Authorization Code時,獲取授權碼。
  • /oauth/token:令牌端點,用於獲取Access Token。
  • /oauth/confirm_access:用於grant_type爲Authorization Code時,用戶確認授權提交端點。
  • /oauth/error:授權服務錯誤信息端點。
  • /oauth/check_token:用於資源服務訪問的令牌解析端點。
  • /oauth/token_key:在JwtTokenStore模式下提供公有密匙的端點。

其他

本文篇幅較長,故先只講解了oauth2-authentication-server模塊。剩下的內容見下一篇文章。

本文是基於springboot+springsecurity+oauth2整合(並用mysql數據庫實現持久化客戶端數據)的教程上進行二次開發的

 

下載項目:https://download.csdn.net/download/qq_37771475/12054521

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章