乐趣区

关于java:SpringCloudOAuth2实现微服务认证太简单了

欢送微信搜寻公众号【java 版 web 我的项目】获取资源:java 学习视频 / 设计模式笔记 / 算法手册 /java 我的项目

一、SpringCloud Security 简介

​ Spring Cloud Security 提供了一组原语,用于构建平安的应用程序和服务,而且操作简便。能够在内部(或集中)进行大量配置的申明性模型有助于实现大型合作的近程组件零碎,通常具备地方身份治理服务。它也十分易于在 Cloud Foundry 等服务平台中应用。在 Spring Boot 和 Spring Security OAuth2 的根底上,能够疾速创立实现常见模式的零碎,如单点登录,令牌中继和令牌替换。

性能:

  • 从 Zuul 代理中的前端到后端服务中继 SSO 令牌
  • 资源服务器之间的中继令牌
  • 使 Feign 客户端体现得像 OAuth2RestTemplate(获取令牌等)的拦截器
  • 在 Zuul 代理中配置上游身份验证

二、繁多登录实现

1、在 SpringCloud——服务的注册与发现 Eureka 的根底上做批改

增加 spring-security 反对:

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

增加配置:

server:
  port: 8082
spring:
  application:
    name: eureka-client

eureka:
  client:
    serviceUrl:
     defaultZone: http://localhost:8081/eureka/
#    fetch-registry: false
#    register-with-eureka: false


# 平安认证的配置
security:
  basic:
    enabled: true

2、启动工程,浏览器拜访:http://localhost:8082/test

输出用户名和明码认证,用户名为 user,明码在程序启动时会输入到管制台上,如图:

登录胜利后浏览器显示:

test=============8082

三、配置 MySQL 数据库实现认证

1、增加相干依赖:

<?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>com.example.demo.security</groupId>
    <artifactId>security-oauth2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>security-oauth2</name>
 
    <parent>
        <groupId>com.example.demo</groupId>
        <artifactId>springcloud-security</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-velocity</artifactId>
            <version>1.1.3.RELEASE</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
 
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2、增加配置文件:

server:
  port: 8081
 
spring:
  application:
    name: security-oauth2
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/alan_oauth?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true&autoReconnect=true
    username: root
    password: admin
    driver-class-name: com.mysql.jdbc.Driver
 
security:
  basic:
    enabled: false

3、相干 config 配置文件

创立受权配置信息类 OAuthSecurityConfig.java,申明 TokenStore 实现和 ClientDetails 的实现。

package com.example.demo.security.config;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
 
import javax.sql.DataSource;
import java.util.concurrent.TimeUnit;
 
/**
 * 门路:com.example.demo.security.config
 * 类名:* 性能:《用一句形容一下》* 备注:* 创建人:typ
 * 创立工夫:2018/9/26 14:25
 * 批改人:* 批改备注:* 批改工夫:*/
@Configuration
public class OAuthSecurityConfig extends AuthorizationServerConfigurerAdapter{
 
    @Autowired
    private AuthenticationManager authenticationManager;
 
    @Autowired
    private DataSource dataSource;
 
    @Bean
    public TokenStore tokenStore(){return new JdbcTokenStore(dataSource);
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManager);
        endpoints.tokenStore(tokenStore());
 
        // 配置 TokenServices 参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(false);
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30 天
        endpoints.tokenServices(tokenServices);
 
    }
 
 
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {//oauthServer.checkTokenAccess("isAuthenticated()");
        oauthServer.checkTokenAccess("permitAll()");
        oauthServer.allowFormAuthenticationForClients();}
 
    @Bean
    public ClientDetailsService clientDetails() {return new JdbcClientDetailsService(dataSource);
    }
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(clientDetails());
        clients.inMemory()
                .withClient("client")
                .secret("secret")
                .authorizedGrantTypes("authorization_code")
                .scopes("app");
    }
}

平安服务配置类 OAuthWebConfig.java

package com.example.demo.security.config;
 
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
 
/**
 * 门路:com.example.demo.security.config
 * 类名:* 性能:《用一句形容一下》* 备注:* 创建人:typ
 * 创立工夫:2018/9/26 14:22
 * 批改人:* 批改备注:* 批改工夫:*/
@Configuration
public class OAuthWebConfig extends WebSecurityConfigurerAdapter{
 
    @Override
    public void configure(WebSecurity web) throws Exception {web.ignoring()
                .antMatchers("/favor.ico");
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {super.configure(http);
    }
}

自定义 provider 调用类 SsoAuthProvider.java

package com.example.demo.security.config;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
 
import java.util.Collections;
 
/**
 * 门路:com.example.demo.security.config
 * 类名:* 性能:《用一句形容一下》* 备注:* 创建人:typ
 * 创立工夫:2018/9/26 14:33
 * 批改人:* 批改备注:* 批改工夫:*/
@Component
public class SsoAuthProvider implements AuthenticationProvider{private static final Logger log = LoggerFactory.getLogger(SsoAuthProvider.class);
 
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {log.info("自定义 provider 调用");
        //// 返回一个 Token 对象示意登陆胜利
        return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), Collections.<GrantedAuthority>emptyList());
    }
 
    @Override
    public boolean supports(Class<?> aClass) {return true;}
}

4、须要一个重定向的 controller 类

package com.example.demo.security.controller;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttributes;
 
import java.util.Map;
 
/**
 * 门路:com.example.demo.security.controller
 * 类名:* 性能:《用一句形容一下》* 备注:* 创建人:typ
 * 创立工夫:2018/9/26 14:30
 * 批改人:* 批改备注:* 批改工夫:*/
@RestController
@SessionAttributes("authorizationRequest")
public class ErrorController {private static final Logger log = LoggerFactory.getLogger(ErrorController.class);
 
    @RequestMapping("/oauth/error")
    public String error(@RequestParam Map<String, String> parameters){String url = parameters.get("redirect_uri");
        log.info("重定向: {}", url);
        return "redirect:" + url + "?error=1";
    }
}

5、启动类增加注解 @EnableAuthorizationServer

package com.example.demo.security;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
 
import java.util.Arrays;
 
@SpringBootApplication
@EnableAuthorizationServer
public class SecurityOauth2Application {public static void main(String[] args) {SpringApplication.run(SecurityOauth2Application.class, args);
    }
 
    @Autowired
    private AuthenticationProvider authenticationProvider;
 
    @Bean
    public AuthenticationManager authenticationManager(){return new ProviderManager(Arrays.asList(authenticationProvider));
    }
}

6、创立数据库及相干表

# Host: 127.0.0.1  (Version 5.7.21)
# Date: 2018-09-26 15:17:51
# Generator: MySQL-Front 6.0  (Build 2.20)
 
 
#
# Structure for table "clientdetails"
#
 
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails` (`appId` varchar(128) NOT NULL,
  `resourceIds` varchar(256) DEFAULT NULL,
  `appSecret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `grantTypes` varchar(256) DEFAULT NULL,
  `redirectUrl` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additionalInformation` varchar(4096) DEFAULT NULL,
  `autoApproveScopes` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "clientdetails"
#
 
 
#
# Structure for table "oauth_access_token"
#
 
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (`token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(128) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  `authentication` blob,
  `refresh_token` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_access_token"
#
 
 
#
# Structure for table "oauth_approvals"
#
 
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (`userId` varchar(256) DEFAULT NULL,
  `clientId` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `status` varchar(10) DEFAULT NULL,
  `expiresAt` datetime DEFAULT NULL,
  `lastModifiedAt` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_approvals"
#
 
 
#
# Structure for table "oauth_client_details"
#
 
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (`client_id` varchar(128) NOT NULL,
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `authorized_grant_types` varchar(256) DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_client_details"
#
 
INSERT INTO `oauth_client_details` VALUES ('client',NULL,'secret','app','authorization_code','http://www.baidu.com',NULL,NULL,NULL,NULL,NULL);
 
#
# Structure for table "oauth_client_token"
#
 
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (`token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(128) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_client_token"
#
 
 
#
# Structure for table "oauth_code"
#
 
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (`code` varchar(256) DEFAULT NULL,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_code"
#
 
 
#
# Structure for table "oauth_refresh_token"
#
 
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (`token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
#
# Data for table "oauth_refresh_token"
#
 

在 oauth_client_details 表中增加一条数据

INSERT INTO `oauth_client_details` VALUES ('client',NULL,'secret','app','authorization_code','http://www.baidu.com',NULL,NULL,NULL,NULL,NULL);

启动工程,浏览器拜访:

http://localhost:8081/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com

点击 Authorize 会跳转到百度页面,因为数据库中配置的是百度页面。

四、OAuth 的思路

OAuth 在 ” 客户端 ” 与 ” 服务提供商 ” 之间,设置了一个受权层(authorization layer)。” 客户端 ” 不能间接登录 ” 服务提供商 ”,只能登录受权层,以此将用户与客户端辨别开来。” 客户端 ” 登录受权层所用的令牌(token),与用户的明码不同。用户能够在登录的时候,指定受权层令牌的权限范畴和有效期。

“ 客户端 ” 登录受权层当前,” 服务提供商 ” 依据令牌的权限范畴和有效期,向 ” 客户端 ” 凋谢用户贮存的材料。

OAuth 2.0 的运行流程:

(A)用户关上客户端当前,客户端要求用户给予受权。

(B)用户批准给予客户端受权。

(C)客户端应用上一步取得的受权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证当前,确认无误,批准发放令牌。

(E)客户端应用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,批准向客户端凋谢资源。

在下面六个步骤之中,B 是要害,即用户怎样才能给于客户端受权。有了这个受权当前,客户端就能够获取令牌,进而凭令牌获取资源。

五、客户端的受权模式

客户端必须失去用户的受权(authorization grant),能力取得令牌(access token)。

OAuth 2.0 定义了四种受权形式:

  • 受权码模式(authorization code)
  • 简化模式(implicit)
  • 明码模式(resource owner password credentials)
  • 客户端模式(client credentials)

1、受权码模式

受权码模式(authorization code)是性能最残缺、流程最紧密的受权模式。它的特点就是通过客户端的后盾服务器,与 ” 服务提供商 ” 的认证服务器进行互动。

步骤如下:

(A)用户拜访客户端,后者将前者导向认证服务器。

(B)用户抉择是否给予客户端受权。

(C)假如用户给予受权,认证服务器将用户导向客户端当时指定的 ” 重定向 URI”(redirection URI),同时附上一个受权码。

(D)客户端收到受权码,附上新近的 ” 重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后盾的服务器上实现的,对用户不可见。

(E)认证服务器核查了受权码和重定向 URI,确认无误后,向客户端发送拜访令牌(access token)和更新令牌(refresh token)。

2、简化模式

简化模式(implicit grant type)不通过第三方应用程序的服务器,间接在浏览器中向认证服务器申请令牌,跳过了 ” 受权码 ” 这个步骤,因而得名。所有步骤在浏览器中实现,令牌对访问者是可见的,且客户端不须要认证。

步骤如下:

(A)客户端将用户导向认证服务器。

(B)用户决定是否给于客户端受权。

(C)假如用户给予受权,认证服务器将用户导向客户端指定的 ” 重定向 URI”,并在 URI 的 Hash 局部蕴含了拜访令牌。

(D)浏览器向资源服务器发出请求,其中不包含上一步收到的 Hash 值。

(E)资源服务器返回一个网页,其中蕴含的代码能够获取 Hash 值中的令牌。

(F)浏览器执行上一步取得的脚本,提取出令牌。

(G)浏览器将令牌发给客户端。

3、明码模式

明码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供本人的用户名和明码。客户端应用这些信息,向 ” 服务商提供商 ” 索要受权。

在这种模式中,用户必须把本人的明码给客户端,然而客户端不得贮存明码。这通常用在用户对客户端高度信赖的状况下,比方客户端是操作系统的一部分,或者由一个驰名公司出品。而认证服务器只有在其余受权模式无奈执行的状况下,能力思考应用这种模式。

步骤如下:

(A)用户向客户端提供用户名和明码。

(B)客户端将用户名和明码发给认证服务器,向后者申请令牌。

(C)认证服务器确认无误后,向客户端提供拜访令牌。

4、客户端模式

客户端模式(Client Credentials Grant)指客户端以本人的名义,而不是以用户的名义,向 ” 服务提供商 ” 进行认证。严格地说,客户端模式并不属于 OAuth 框架所要解决的问题。在这种模式中,用户间接向客户端注册,客户端以本人的名义要求 ” 服务提供商 ” 提供服务,其实不存在受权问题。

步骤如下:

(A)客户端向认证服务器进行身份认证,并要求一个拜访令牌。

(B)认证服务器确认无误后,向客户端提供拜访令牌。

六、更新令牌

如果用户拜访的时候,客户端的 ” 拜访令牌 ” 曾经过期,则须要应用 ” 更新令牌 ” 申请一个新的拜访令牌。

客户端收回更新令牌的 HTTP 申请,蕴含以下参数:

  • granttype:示意应用的受权模式,此处的值固定为 ”refreshtoken”,必选项。
  • refresh_token:示意早前收到的更新令牌,必选项。
  • scope:示意申请的受权范畴,不能够超出上一次申请的范畴,如果省略该参数,则示意与上一次统一。

作者:程序员大本营

起源:https://www.pianshen.com/arti…

近期热文举荐:

SpringCloud 微服务电商我的项目教程

退出移动版