Серия OAuth2.0 интегрировала JWT для обеспечения единого входа

задняя часть

TOC

Блог серии OAuth2.0:

Схема реализации: OAuth2.0+JWT+Spring Security

Серия блогов SpringBoot:улыбка Nicky.blog.CSDN.net/article/decent…, серия блогов о едином входе:blog.CSDN.net/U014427391/…

1. Введение в систему единого входа

1.1 Определение единого входа

Единый вход (Single sign on), английская аббревиатура имени SSO, SSO означает, что в мультисистемной среде, войдя в одностороннюю систему, вы можете получить доступ к связанным доверенным системам без повторного входа в систему. То есть вам нужно войти в единую систему только один раз.

1.2 Роли единого входа

Единый вход обычно включает в себя следующие три роли:

① Пользователь (несколько);

②Центр сертификации (один);

③ Веб-приложение (несколько).

PS: Упомянутое здесь веб-приложение можно понимать как SSO-клиент, а центр аутентификации можно назвать SSO-сервером.

1.3 Классификация единого входа

Поскольку протокол http является протоколом без сохранения состояния, для поддержания состояния входа в систему необходимо хранить информацию для входа.В соответствии с методом хранения реализация единого входа в основном может быть разделена на два типа.

  • Один из них основан на куки-файлах, что является относительно распространенным.Например, CAS, описанный ниже, также основан на куки-файлах;
  • Другой основан на сеансе,На самом деле это понимается как разделение сеансов, только реализуя совместное использование сеансов между различными подсистемами, можно реализовать единый вход., за подробностями обратитесь к моему предыдущему блогу, посвященному совместному использованию сеансов и единому входу,blog.CSDN.net/U014427391/…

2. ОAuth2.0

2.1 Введение в OAuth2.0

OAuth — это открытый протокол, который позволяет пользователям разрешать сторонним приложениям получать доступ к секретным ресурсам (таким как личная информация пользователя, фотографии, видео, списки контактов), которые пользователь хранит на веб-сайте, в мобильном или настольном приложении безопасным и стандартным способом. ) без предоставления имени пользователя и пароля стороннему приложению.

Официальный сайт:oauth.net/2/, на официальном сайте есть только английская версия документов, вы также можете обратиться к переведенным документам, ссылка:OAuth2 RFC6749 перевод на китайский язык

Протокол OAuth 1.0 (RFC5849) был опубликован в качестве руководящего документа и является работой небольшого сообщества. Эта стандартизированная спецификация основана на опыте развертывания OAuth 1.0, а также на других вариантах использования и требованиях к расширяемости, полученных от более широкого сообщества IETF. Протокол OAuth 2.0 не имеет обратной совместимости с OAuth 1.0. Обе версии могут сосуществовать в сети, и разработчики могут выбрать поддержку обеих.

OAuth2.0 значительно улучшил безопасность.Короче говоря, OAuth2.0 — это протокол авторизации, который можно использовать для авторизации.Кстати, щелкните веб-сайт, например WeChat и вход в Alipay, как показано на рисунке, которые приложения OAuth2.0

在这里插入图片描述

2.2 Роли OAuth2.0

OAuth2.0 определяет следующие роли:

  • Владелец ресурса: сущность, которая может предоставлять доступ к защищенным ресурсам. Когда владельцем ресурса является физическое лицо, его называют конечным пользователем.
  • Пользовательский агент: относится к некоторым каналам, авторизованным владельцем ресурса. Обычно относится к браузеру, APP
  • Клиент (Клиент) Приложение, которое инициирует запрос защищенного ресурса от имени владельца ресурса, используя авторизацию владельца ресурса. Термин «клиент» конкретно не относится к какой-либо конкретной функции реализации (например, к тому, выполняется ли приложение на сервере, рабочем столе или другом устройстве).
  • Сервер авторизации: сервер, который выдает токен доступа клиенту после успешной аутентификации владельца ресурса и получения авторизации. Взаимодействие между серверами авторизации и серверами ресурсов выходит за рамки данной спецификации. Сервер авторизации может быть тем же сервером, что и сервер ресурсов, или отдельным объектом. Сервер авторизации может выдавать маркеры доступа, которые принимаются несколькими серверами ресурсов.
  • Сервер ресурсов: сервер, на котором размещаются защищенные ресурсы, способный получать и отвечать на запросы защищенных ресурсов с использованием токенов доступа.

2.3 Протокольный процесс

在这里插入图片描述

ЦитироватьOAuth2 RFC6749 перевод на китайский язык: (A) Клиент запрашивает авторизацию у владельца ресурса. Запросы на авторизацию можно направлять непосредственно владельцу ресурса (как показано) или, что предпочтительнее, косвенно через сервер авторизации, выступающий в роли посредника. (B) Клиент получает разрешение на авторизацию, которое представляет собой учетные данные, представляющие авторизацию владельца ресурса, с использованием одного из четырех типов разрешений, определенных в этой спецификации, или с использованием расширенного типа разрешения. Тип разрешения на авторизацию зависит от того, как клиент запрашивает авторизацию и какой тип авторизации поддерживается сервером авторизации. (C) Клиент аутентифицируется на сервере авторизации и представляет запрос на авторизацию для токена доступа. (D) Сервер авторизации проверяет личность клиента и авторизацию, а также выдает токен доступа, если он действителен. (E) Клиент запрашивает защищенный ресурс с сервера ресурсов и представляет маркер доступа для аутентификации. (F) Сервер ресурсов проверяет токен доступа и, если он действителен, выполняет запрос.

2.4 Режим авторизации

OAuth2.0 имеет 4 режима авторизации:

  • Код авторизации
  • Неявный грант
  • Учетные данные пароля владельца ресурса
  • учетные данные клиента

Наиболее часто используется режим кода авторизации.Подробное введение в режим авторизации можно найти у г-на Руана Ифэна:Четыре способа OAuth 2.0

Блок-схема режима официального кода авторизации:

在这里插入图片描述

Три, реализация единого входа

3.1 Подготовка окружающей среды

  • IntelliJ IDEA
  • Версия Maven3.+ Создайте новый проект SpringBoot Initializer.
    在这里插入图片描述

在这里插入图片描述
В основном хочу представить:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 <!-- Spring Cloud Oauth2-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <!-- Spring Cloud Security-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

Скрипты схемы: все скрипты предоставлены официально:Ссылка на сайт, официальным скриптом является база данных HSQL, поэтому мы изменили ее на версию mysql, например код ссылки:


-- used in tests that use MYSQL
CREATE TABLE oauth_client_details (
  client_id VARCHAR(128) PRIMARY KEY,
  resource_ids VARCHAR(128),
  client_secret VARCHAR(128),
  scope VARCHAR(128),
  authorized_grant_types VARCHAR(128),
  web_server_redirect_uri VARCHAR(128),
  authorities VARCHAR(128),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  ENABLE TINYINT(1) DEFAULT '1',
  autoapprove VARCHAR(128)
);

CREATE TABLE oauth_client_token (
  token_id VARCHAR(128),
  token BLOB,
  authentication_id VARCHAR(128) PRIMARY KEY,
  user_name VARCHAR(128),
  client_id VARCHAR(128)
);

CREATE TABLE oauth_access_token (
  token_id VARCHAR(128),
  token BLOB,
  authentication_id VARCHAR(128) PRIMARY KEY,
  user_name VARCHAR(128),
  client_id VARCHAR(128),
  authentication BLOB,
  refresh_token VARCHAR(128)
);


CREATE TABLE oauth_refresh_token (
  token_id VARCHAR(128),
  token BLOB,
  authentication BLOB
);


CREATE TABLE oauth_code (
  CODE VARCHAR(128), authentication BLOB
);

CREATE TABLE oauth_approvals (
	userId VARCHAR(128),
	clientId VARCHAR(128),
	scope VARCHAR(128),
	STATUS VARCHAR(10),
	expiresAt TIMESTAMP,
	lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP  
);

-- customized oauth_client_details table
CREATE TABLE ClientDetails (
  appId VARCHAR(128) PRIMARY KEY,
  resourceIds VARCHAR(128),
  appSecret VARCHAR(128),
  scope VARCHAR(128),
  grantTypes VARCHAR(128),
  redirectUrl VARCHAR(128),
  authorities VARCHAR(128),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additionalInformation VARCHAR(4096),
  autoApproveScopes VARCHAR(128)
);

В этом блоге используется только таблица oauth_client_details.

3.2 Сервер авторизации OAuth2.0

3.2.1 Создайте новый проект SpringBoot

Создайте новый проект SpringBoot Initializer, который можно назвать Oauth2-server.

在这里插入图片描述

在这里插入图片描述

3.2.2 Аннотация @EnableResourceServer

Класс запуска помечен @EnableResourceServer:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@SpringBootApplication
@EnableResourceServer
public class JeeplatformSsoOauth2Application {

    public static void main(String[] args) {
        SpringApplication.run(JeeplatformSsoOauth2Application.class, args);
    }

}

3.2.3 Класс конфигурации OAuth2.0

Подготовка среды, вам нужно написать некоторые данные конфигурации.Если вы хотите использовать метод памяти, вам нужно реализовать его в коде.

insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('cms',NULL,'{noop}secret','all','authorization_code','http://localhost:8084/cms/login',NULL,'60','60',NULL,'true');
insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('oa',NULL,'{noop}secret','all','authorization_code','http://localhost:8082/oa/login',NULL,'60','60',NULL,'true');

Класс конфигурации OAuth2.0:

package org.muses.jeeplatform.oauth.configuration;

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.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 *  OAuth2.0配置
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/04/29 15:06  修改内容:
 * </pre>
 */
@Configuration
//开启授权服务
@EnableAuthorizationServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Resource(name = "userService")
    private UserDetailsService userDetailsService;

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;

    private static final String CLIENT_ID = "cms";
    private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
    private static final String SCOPE_READ = "read";
    private static final String SCOPE_WRITE = "write";
    private static final String TRUST = "trust";
    private static final String USER ="user";
    private static final String ALL = "all";
    private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 2*60;
    private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60;
    // 密码模式授权模式
    private static final String GRANT_TYPE_PASSWORD = "password";
    //授权码模式
    private static final String AUTHORIZATION_CODE = "authorization_code";
    //refresh token模式
    private static final String REFRESH_TOKEN = "refresh_token";
    //简化授权模式
    private static final String IMPLICIT = "implicit";
    //指定哪些资源是需要授权验证的
    private static final String RESOURCE_ID = "resource_id";

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /*clients
                // 使用内存存储
                .inMemory()
                //标记客户端id
                .withClient(CLIENT_ID)
                //客户端安全码
                .secret(SECRET_CHAR_SEQUENCE)
                //为true 直接自动授权成功返回code
                .autoApprove(true)
                .redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
                //允许授权范围
                .scopes(ALL)
                //token 时间秒
                .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                //刷新token 时间 秒
                .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
                //允许授权类型
                .authorizedGrantTypes(GRANT_TYPE_PASSWORD , AUTHORIZATION_CODE , REFRESH_TOKEN , IMPLICIT);*/
        // 数据库保存配置信息到oauth_client_details表,schema参考sql/oauth_client_details
        clients.jdbc(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(jwtTokenStore()).authenticationManager(authenticationManager)
                .accessTokenConverter(accessTokenConverter())
                //必须注入userDetailsService否则根据refresh_token无法加载用户信息
                .userDetailsService(userDetailsService)
                //支持获取token方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS)
                //刷新token
                .reuseRefreshTokens(true);
                //endpoints.tokenServices(createDefaultTokenServices());
        // 使用内存保存生成的token
        //endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
    }

    /**
     * 认证服务器的安全配置
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //.realm(RESOURCE_ID)
                // 开启/oauth/token_key验证端口认证权限访问
                .tokenKeyAccess("isAuthenticated()")
                //  开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                //允许表单认证 在授权码模式下会导致无法根据code获取token&emsp;
                .allowFormAuthenticationForClients();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                String grantType = authentication.getOAuth2Request().getGrantType();
                //授权码和密码模式才自定义token信息
                if(AUTHORIZATION_CODE.equals(grantType) || GRANT_TYPE_PASSWORD.equals(grantType)) {
                    String userName = authentication.getUserAuthentication().getName();
                    // 自定义一些token 信息
                    Map<String, Object> additionalInformation = new HashMap<String, Object>(16);
                    additionalInformation.put("user_name", userName);
                    additionalInformation = Collections.unmodifiableMap(additionalInformation);
                    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                }
                OAuth2AccessToken token = super.enhance(accessToken, authentication);
                return token;
            }
        };
        // 设置签署key
        converter.setSigningKey("bcrypt");
        return converter;
    }

    @Bean
    public TokenStore jwtTokenStore() {
        //基于jwt实现令牌(Access Token)保存
        return new JwtTokenStore(accessTokenConverter());
    }

//    @Bean
//    public TokenStore memoryTokenStore() {
//        // 最基本的InMemoryTokenStore生成token
//        return new InMemoryTokenStore();
//    }


}

3.2.4 Конфигурация безопасности Spring

package org.muses.jeeplatform.oauth.configuration;


import org.muses.jeeplatform.oauth.component.CustomPasswordEncoder;
import org.muses.jeeplatform.oauth.filter.SimpleCORSFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.web.context.SecurityContextPersistenceFilter;


import javax.annotation.Resource;

/**
 * <pre>
 *
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/04/30 15:58  修改内容:
 * </pre>
 */
@Configuration
@EnableWebSecurity
@Order(1)
//@EnableGlobalMethodSecurity(prePostEnabled = true)
//@EnableAutoConfiguration(exclude = {
//        org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class })
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private SimpleCORSFilter simpleCORSFilter;

    @Resource(name = "userService")
    private UserDetailsService userDetailsService;

    private static final String SECRET_CHAR_SEQUENCE = "secret";

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


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {    //auth.inMemoryAuthentication()
//        auth.inMemoryAuthentication()
//                .withUser("nicky")
//                .password("{noop}123")
//                .roles("admin");
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new CustomPasswordEncoder());
        auth.parentAuthenticationManager(authenticationManagerBean());

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/asserts/**");
        //web.ignoring().antMatchers("/favicon.ico");

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http   // 配置登录页并允许访问
                .formLogin().loginPage("/login").permitAll()
                // 配置Basic登录
                //.and().httpBasic()
                // 配置登出页面
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
                // 其余所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                // 关闭跨域保护;
                .and().csrf().disable();
        //http.addFilterBefore(simpleCORSFilter, SecurityContextPersistenceFilter.class);
    }

}

Класс UserDetailsServiceImpl:

package org.muses.jeeplatform.oauth.service;

import lombok.extern.slf4j.Slf4j;
import org.muses.jeeplatform.oauth.entity.User;
import org.muses.jeeplatform.oauth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;

import java.util.Arrays;
import java.util.List;

/**
 * <pre>
 *
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/04/30 15:15  修改内容:
 * </pre>
 */
@Slf4j
@Service("userService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserMapper userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if(user == null){
            log.info("登录用户[{}]没注册!",username);
            throw new UsernameNotFoundException("登录用户["+username + "]没注册!");
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthority());
    }

    private List getAuthority() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
//        return Arrays.asList(Collections.emptyList());
    }
}

Пользовательское шифрование, здесь можно использовать MD5 или соль, я написал собственный класс шифрования, для удобства тестирования, никакого шифрования:

package org.muses.jeeplatform.oauth.component;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * <pre>
 *   自定义PasswordEncoder
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/04/24 17:02  修改内容:
 * </pre>
 */
public class CustomPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        String encodeStr = charSequence.toString() + "";
        if (encodeStr.equals(s)) {
            return true;
        }
        return false;
    }
}

3.2.5 Настройка страницы входа

Обратите внимание, что вы должны найти интерфейс GET, потому что интерфейс аутентификации OAuth2.0 также /login, но это POST

 @GetMapping(value = {"/login"})
    public ModelAndView toLogin(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("login");
        return modelAndView;
    }

Целевая страница с использованием шаблонизатора Themeleaf, ключевых точек,th:action="@{/login}" method="post", обратите внимание на использование метода POST

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
		<meta name="description" content="" />
		<meta name="author" content="" />
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link href="../static/asserts/css/bootstrap.min.css" th:href="@{asserts/css/bootstrap.min.css}" rel="stylesheet" />
		<!-- Custom styles for this template -->
		<link href="../static/asserts/css/signin.css" th:href="@{asserts/css/signin.css}" rel="stylesheet"/>
	</head>

	<body class="text-center">
		<form class="form-signin" th:action="@{/login}" method="post">
			<img class="mb-4" th:src="@{asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72" />
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{messages.tip}">Oauth2.0 Login</h1>
			<label class="sr-only" th:text="#{messages.username}">Username</label>
			<input type="text" class="form-control" name="username" th:placeholder="#{messages.username}" required="" autofocus="" value="nicky" />
			<label class="sr-only" th:text="#{messages.password} ">Password</label>
			<input type="password" class="form-control" name="password" th:placeholder="#{messages.password}" required="" value="123" />
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" value="remember-me"  /> remember me
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{messages.loginBtnName}">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2019</p>
			<a class="btn btn-sm" th:href="@{/login(lang='zh_CN')} ">中文</a>
			<a class="btn btn-sm" th:href="@{/login(lang='en_US')} ">English</a>
		</form>

	</body>

</html>

3.2.6 Тестовый сервер

Тест: интерфейс авторизации

http://localhost:8888/oauth/authorize?client_id=cms&client_secret=secret&response_type=code

在这里插入图片描述

Если пароль учетной записи правильный, uri перенаправления будет возвращен с кодом авторизации:http://localhost:8084/cms/login?code=UyRvO9

Интерфейс получения токена:

在这里插入图片描述
Используйте код авторизации для настройки интерфейса токена и успешного получения токена.
在这里插入图片描述
При использовании метода JWT формат токена выглядит следующим образом: JWT:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1MzQ5NzMsInVzZXJfbmFtZSI6Im5pY2t5IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9hZG1pbiJdLCJqdGkiOiJmMjM0M2Q0NC1hODViLTQyOGYtOWE1ZS1iNTE4NTAwNTM5ODgiLCJjbGllbnRfaWQiOiJvYSIsInNjb3BlIjpbImFsbCJdfQ.LWkN2gC2dBrGTn5uSPzfdW6yRj7jhlX87EE8scY02hI",
    "token_type": "bearer",
    "expires_in": 59,
    "scope": "all",
    "user_name": "nicky",
    "jti": "f2343d44-a85b-428f-9a5e-b51850053988"
}

3.3 Подключение клиента OAuth2.0

3.3.1 Создайте новый проект SpringBoot

Создайте новый проект SpringBoot Initializer, который можно назвать Oauth2-Client1.

在这里插入图片描述

在这里插入图片描述

3.3.2 конфигурация приложения

security.oauth2.client.client-secret=secret
security.oauth2.client.client-id=cms
security.oauth2.client.scope=all
security.oauth2.client.user-authorization-uri=http://localhost:8888/oauth/authorize
security.oauth2.client.access-token-uri=http://localhost:8888/oauth/token
security.oauth2.resource.user-info-uri=http://localhost:8888/user
security.oauth2.client.use-current-uri=false
security.oauth2.client.pre-established-redirect-uri=http://localhost:8084/cms/login
security.oauth2.resource.token-info-uri= http://localhost:8888/oauth/check_token
security.oauth2.resource.jwt.key-uri=http://localhost:8888/oauth/token_key
security.oauth2.authorization.check-token-access= http://localhost:8888/oauth/check_token

server.port=8084
server.servlet.context-path=/cms

# 预防cookie冲突,设置cookie name
server.servlet.session.cookie.name=OAUTH2-CLIENT-SESSIONID

3.3.3 Реализация класса конфигурации

Ключевая аннотация — @EnableOAuth2Sso, необходимо добавить единый вход.

package org.muses.jeeplatform.cms.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

/**
 * <pre>
 *
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/05/07 16:08  修改内容:
 * </pre>
 */
@Configuration
@EnableOAuth2Sso
//@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.logout().logoutSuccessUrl("http://localhost:8888/logout")
                    .and()
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .csrf().disable();
       
    }
}

3.3.4 Тест единого входа

Напишите тестовый интерфейс:

  @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication) {
        return authentication;
    }

http://localhost:8084/cms/getCurrentUser

Обычно он перескакивает на страницу входа.После проверки он возвращается.Чтобы проверить одну точку, вам нужно создать новый Клиент, а затем прописать данные конфигурации:

insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('oa',NULL,'{noop}secret','all','authorization_code','http://localhost:8082/oa/login',NULL,'60','60',NULL,'true');

Таким же образом, если клиент cms уже вошел в систему, клиенту OA не нужно входить снова.

4. Справочная информация блога

Загрузите пример кода этого блога:code download

Уууу. Руан Ифэн.com/blog/2019/0… wiki.open.QQ.com/wiki/Веб — это ИТ… блог woo woo woo.cn на.com/sky-chen/rub… проекты.spring.IO/spring-brush-ECU…