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_adminpasswordROLE_OAUTH_ADMIN
resource_adminpasswordROLE_RESOURCE_ADMIN
product_adminpasswordROLE_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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值