您现在的位置是:首页 >其他 >Spring Security实现OAuth2协议及实战网站首页其他
Spring Security实现OAuth2协议及实战
Spring Security实现OAuth2协议及实战
文章篇幅较长,愿读者耐心看完。如有不足之处,请指正。
一.OAuth2介绍
1.1 OAuth2是什么 怎么用
OAuth2是目前最流行的授权协议,用来授权第三方应用,获取用户数据。
举个例子:快递员想要进入小区,有3种方式。1是业主远程开门,2是业主告诉门禁密码,3是使用令牌(Oauth2)。
如图:
令牌和密码的区别:令牌相当于火车票,密码相当于是钥匙。
令牌是短期的,自动失效。密码是长期有效。
令牌是可以撤销的,撤销立即生效。密码一般不允许他们撤销。
令牌有权限范围,如车票座位为10车A15座。密码一般是完整权限。
第三方登录演示(网易云客户端利用QQ扫码登录)
网易云客使用QQ扫码登录中Oauth2协议各个角色扮演者
Rrsource Owner: 用户
Client: 网易云
Authorization Server: QQ
Resource Server: QQ
User Agent: 浏览器
Oauth2.0与1.0的区别:
OAuth2.0有4种授权模式。优缺点:1.去掉签名,改用SSL(HTTPS)确保安全性。2.所有的token不再有对应的secret存在,签名过程简洁,这也直接导致OAuth2.0不能兼容老版本。3.能更好的支持不是基于浏览器的应用。4.OAuth2.0的访问令牌是"短命的",且可以刷新令牌。
OAuth1.0只有一种授权模式。优缺点:用的是http协议,申请RequestToken过程中,容易把攻击者调包。2.攻击者调包后的目的是为了仿造回调地址,拿到用户的accessToken。
Oauth2授权模式:
授权码模式:最完整和严谨的授权模式,第三方平台登录都是该模式。安全性最高。
简化模式:省略授权码阶段,客户端是纯静态页面采用该模式。安全性高。
密码模式:把用户名密码告诉客户端,对客户端高度信任,比如客户端和认证服务器是同一公司。安全性一般。
客户端模式:直接因客户端名义申请令牌,很少有。安全性最差。
1.2 为什么要用OAuth2
cookie是不能跨域的,前后端分离分布式架构实现多系统SSO非常困难。
移动端应用没有cookie,所以对于移动端支持不友好。
token基于header传递,部分解决了CSRF攻击。
token比sessionID大,客户端存储在Local Storage中,可以直接被JS读取。
二.OAuth2实战
2.1 搭建项目
首先创建两个项目 一个是授权项目 一个是资源项目
授权项目目录(java-skill-point)
pom.xml文件
这里要注意springboot与springcloud的版本号
<?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 https://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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.weige</groupId>
<artifactId>java-skill-point</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>java-skill-point</name>
<description>java-skill-point</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<!--Springboot项目自带 -->
<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>
<!--Springboot Web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml文件
server:
port: 8888
OAuth2Config文件
package com.weige.javaskillpoint.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.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.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
private static final String CLIENT_ID = "cms";
private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
private static final String ALL = "all";
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 30 * 60;
// 密码模式授权模式
private static final String GRANT_TYPE_PASSWORD = "password";
//授权码模式
private static final String AUTHORIZATION_CODE = "authorization_code";
//简化授权模式
private static final String IMPLICIT = "implicit";
//客户端模式
private static final String CLIENT_CREDENTIALS = "client_credentials";
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(CLIENT_ID)
.secret(SECRET_CHAR_SEQUENCE)
.autoApprove(false)
.redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
.scopes(ALL)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.authorizedGrantTypes(AUTHORIZATION_CODE, IMPLICIT, GRANT_TYPE_PASSWORD, CLIENT_CREDENTIALS);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
}
/**
* 认证服务器的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 开启/oauth/check_token验证端口认证权限访问,checkTokenAccess("isAuthenticated()")设置授权访问
.checkTokenAccess("permitAll()")
//允许表单认证
.allowFormAuthenticationForClients();
}
@Bean
public TokenStore memoryTokenStore() {
return new InMemoryTokenStore();
}
}
SecurityConfig文件
package com.weige.javaskillpoint.config;
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.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;
@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication()
auth.inMemoryAuthentication()
.withUser("lxs")
.password("{noop}123") //使用springsecurity5,需要加上{noop}指定使用NoOpPasswordEncoder给DelegatingPasswordEncoder去校验密码
.roles("admin");
}
@Override
public void configure(WebSecurity web) throws Exception {
//解决静态资源被拦截的问题
// web.ignoring().antMatchers("/asserts/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
.and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**", "/api/**").permitAll()
.anyRequest().authenticated()
// 关闭跨域保护;
.and().csrf().disable();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
授权认证项目代码码完了 接下来给大家介绍代码的含义 重点是OAuth2Config与SecurityConfig两个类
OAuth2Config代码介绍:
SecurityConfig代码介绍:
资源项目目录(oauth2-service-01)
pom.xml文件
<?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 https://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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.weige</groupId>
<artifactId>java-skill-point</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>java-skill-point</name>
<description>java-skill-point</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<!--Springboot项目自带 -->
<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>
<!--Springboot Web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml文件
server:
port: 8084
Oauth2ResourceServerConfiguration文件
package com.weige.javaskillpoint.config;
import java.io.IOException;
import javax.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableResourceServer
public class Oauth2ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
private static final String CHECK_TOKEN_URL = "http://localhost:8888/oauth/check_token";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl(CHECK_TOKEN_URL);
tokenService.setClientId("cms");
tokenService.setClientSecret("secret");
resources.tokenServices(tokenService);
}
}
SecurityConfiguration文件
package com.weige.javaskillpoint.config;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
// 禁用CSRF
http.csrf().disable();
}
}
HelloController文件
package com.weige.javaskillpoint.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
@RestController
@RequestMapping("/cms")
public class HelloController {
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication) {
return authentication;
}
@GetMapping("/index")
public String index() {
return "index";
}
@PutMapping("/index2")
public String index2() {
return "index2";
}
}
资源项目代码码完了 接下来给大家介绍代码的含义 重点是Oauth2ResourceServerConfiguration与SecurityConfiguration两个类
Oauth2ResourceServerConfiguration代码介绍:
SecurityConfiguration代码介绍:
HelloController类中定义了受限访问的资源
2.2 启动两个项目 并进行接口测试
启动授权服务项目,结果演示:
测试:
随便校验一个令牌:校验接口 http://localhost:8888/oauth/check_token?token=abcdefg123456
启动资源服务项目,访问接口:
没有携带token,访问无权限
2.3 四种授权模式接口演示
授权码模式:
1.申请授权码 localhost:8888/oauth/authorize?client_id=cms&cliect_secret=secret&response_type=code
client_id:客户端id,和授权配置类中设置的客户端id一致。
response_type:授权码模为code。
cliect_secret:客户端密码,和授权配置类中设置的secret一致。
2.根据授权码申请令牌(在PostMan中调用接口):http://localhost:8888/oauth/token?code=ckujfQ&grant_type=authorization_code&redirect_url=http://127.0.0.1:8084/cms/login&scope=all
grant_type:授权类型,填写authorization_code,表示授权码模式。
code:授权码,注意授权码使用一次就无效了,需要重新申请。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数,这里需要。
令牌校验:http://localhost:8888/oauth/check_token?token=4a7e8dbc-24ef-4517-8de3-d9b0229dfa0b
使用令牌:http://localhost:8888/oauth/check_token?token=4a7e8dbc-24ef-4517-8de3-d9b0229dfa0b
携带token则访问资源项目接口成功
简化模式:
申请令牌 浏览器请求:localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://127.0.0.1:8084/cms/login&response_type=token&scope=all
输入网址回车
校验令牌:http://localhost:8888/oauth/check_token?token=f9013048-946b-4875-a83a-69371e7e0829
资源测试
密码模式:
申请令牌 使用PostMan:localhost:8888/oauth/token?grant_type=password&username=lxs&password=123&scope=all
校验令牌:http://localhost:8888/oauth/check_token?token=1365345d-8765-47d4-8d2f-563a31a8b3a7
资源测试
客户端模式:
申请令牌 使用PostMan:localhost:8888/oauth/token?grant_type=password&username=lxs&password=123&scope=all
校验令牌:http://localhost:8888/oauth/check_token?token=5c58a1ef-e1d5-49e7-9ef7-da1063261998
资源测试
三.Spring Security实现OAuth2协议实战(对标大厂)
3.1 生成JWT公钥私钥
1. ⽣成密钥证书 下边命令⽣成密钥证书,采⽤RSA 算法每个证书包含公钥和私钥 创建⼀个⽂件夹,在该⽂件夹下执⾏如下命令⾏:
keytool -genkeypair -alias kaikeba -keyalg RSA -keypass kaikeba -keystore kaikeba.jks -storepass kaikeb
参数解释:
alias:密钥的别名
keyalg:使⽤的hash算法
keypass:密钥的访问密码
keystore:密钥库⽂件名
storepass:密钥库的访问密码
导出公钥
下载网址 https://slproweb.com/products/Win32OpenSSL.html
配置环境变量
cmd进⼊kaikeba.jks⽂件所在⽬录执⾏如下命令 keytool -list -rfc --keystore kaikeba.jks | openssl x509 -inform pem -pubkey
下⾯段内容是公钥
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAojlMgqf4RVL0ExMpJWoO q35eow0lhDFTpUOUkU1ZGWoK+ZwQFUnR5w5U+u2mHqq3dFJWUIAmQC+y5p2ClsvG TbAvYQmL1k4X6oaGj7Bi9SSX6QQeM5bXsrXsjsGJyQyIbqgyBYIg4ZNB29UDcTCv xdFl8+rXoOppqENnTZpij8EIzJooCfrc2GzAeljmgPi4DFJnDAxE4joVz70xWk36 noRHSEfvmUaW+1S1T0cEH+j9p8PUGonnjqU8R6ZPmKAhU1w2t002dLqtkFDzxPW7 M1uJ4hq/CL7smvkOkGb0UVLAwFR9hzO2loxn/y0DcRdyxb5FVPQXnjXCp2hyT0mX pwIDAQAB -----END PUBLIC KEY-----
创建public.key文件 将公钥复制进去
3.2 搭建授权项目与资源项目
搭建授权项目 项目目录
pom.xml
<?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 https://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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.weige</groupId>
<artifactId>java-skill-point</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>java-skill-point</name>
<description>java-skill-point</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<!--Springboot项目自带 -->
<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>
<!--Springboot Web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!--Mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis 用于接口注解快速开发(@Insert @Select @Update) -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--用于扫描mybatis注解 @Mapper @MapperScan-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8888
spring:
main:
allow-bean-definition-overriding: true
# mysql连接信息
datasource:
# mysql8之后
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: ******
大家在这里要创建对应的数据库index 并创建oauth_client_details表
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) COLLATE utf8mb4_german2_ci NOT NULL,
`resource_ids` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`client_secret` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`scope` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`authorized_grant_types` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`web_server_redirect_uri` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`authorities` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) COLLATE utf8mb4_german2_ci DEFAULT NULL,
`autoapprove` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_german2_ci;
INSERT INTO `index`.`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', '', '$2a$10$WZQaLHfS6amrJzN50wE3e.upn8KIi1wmCH9FSdZE6OBt8OKSyGLm.', 'read, write', 'client_credentials,implicit,authorization_code,refresh_token,password', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false');
## '$2a$10$WZQaLHfS6amrJzN50wE3e.upn8KIi1wmCH9FSdZE6OBt8OKSyGLm.' 是加密过后的123456
AdminTokenConstant文件
package com.weige.javaskillpoint.config;
public class AdminTokenConstant {
/**
* 秘钥全名称
*/
public static final String KEY_LOCATION = "kaikeba.jks";
/**
* 密钥的密码,此密码和别名要匹配
*/
public static final String KEY_PASSWORD = "kaikeba";
}
AuthorizationServerConfiguration文件
package com.weige.javaskillpoint.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
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.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.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.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Resource
private AuthenticationManager authenticationManager;
@Resource
private DataSource dataSource;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
// 证书路径和密钥库密码
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(AdminTokenConstant.KEY_LOCATION), AdminTokenConstant.KEY_PASSWORD.toCharArray());
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 密钥别名
converter.setKeyPair(keyStoreKeyFactory.getKeyPair(AdminTokenConstant.KEY_PASSWORD));
return converter;
}
/**
* 声明 ClientDetails实现
*
* @return
*/
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* 配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,
* 你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 直接读取数据库,需要保证数据库配置有客户端信息(oauth_client_details),否则资源服务器无法获取认证数据
clients.withClientDetails(clientDetailsService());
}
/**
* 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services),还有token的存储方式(tokenStore)
*
* @param endpoints
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtAccessTokenConverter()).authenticationManager(authenticationManager);
// 配置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));
endpoints.tokenServices(tokenServices);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
// 允许表单认证
security.allowFormAuthenticationForClients()
// 开启/oauth/token_key验证端口无权限访问
.tokenKeyAccess("permitAll()")
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("permitAll()");
}
}
SecurityConfiguration文件
package com.weige.javaskillpoint.config;
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.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 javax.annotation.Resource;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 所有请求都加入HttpSecurity(多个HttpSecurity过滤)
http.requestMatchers().anyRequest()
// 开放/oauth/开头的所有请求
.and().authorizeRequests().antMatchers("/oauth/**").permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 注入自定义的UserDetailsService,采用BCrypt加密
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
UserDetailServiceImpl文件
package com.weige.javaskillpoint.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class UserDetailServiceImpl implements UserDetailsService {
/**
* 根据用户名加载用户信息
*
* @param username 用户名
* @return 用户详情
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这里可以连接数据库 我这里写假数据
// 有两个用户(username:user,password:user;username:admin,password:admin)
Map<String, String> map = new HashMap<>();
map.put("user", "USER");
map.put("admin", "ADMIN");
String passWord = "$2a$10$b7cqpfkMLYE2H5wxMluGQOrXo76ZfaAALEaemrfFLwDvvHuJuvX/2";
if (username.equals("admin")) {
passWord = "$2a$10$b7cqpfkMLYE2H5wxMluGQOrXo76ZfaAALEaemrfFLwDvvHuJuvX/2";
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
// 获取用户的授权
String s = map.get(username);
// Spring Security 中权限名称必须满足ROLE_XXX
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(StringUtils.join("ROLE_", s));
grantedAuthorities.add(grantedAuthority);
log.info("granted authorities :{} ", grantedAuthorities);
return new User(username, passWord, grantedAuthorities);
}
}
JavaSkillPointApplication文件
package com.weige.javaskillpoint;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.weige.javaskillpoint.dao")
public class JavaSkillPointApplication {
public static void main(String[] args) {
SpringApplication.run(JavaSkillPointApplication.class, args);
}
}
JavaSkillPointApplicationTests测试文件
package com.weige.javaskillpoint;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
class JavaSkillPointApplicationTests {
@Test
public void testCreateJwt() throws Exception {
//证书⽂件
String key_location = "kaikeba.jks";
// 密钥库密码
String keystore_password = "kaikeba";
// 访问证书路径
ClassPathResource resource = new ClassPathResource(key_location);
// 密钥⼯⼚
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, keystore_password.toCharArray());
// 密钥的密码,此密码和别名要匹配
String keypassword = "kaikeba";
//密钥别名
String alias = "kaikeba";
// 密钥对(密钥和公钥)
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keypassword.toCharArray());
// 私钥
RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
// 定义payload信息
Map<String, Object> tokenMap = new HashMap<String, Object>();
tokenMap.put("id", "123");
tokenMap.put("name", "malong");
tokenMap.put("roles", "r01,r02");
tokenMap.put("ext", "1");
// ⽣成jwt令牌
Jwt jwt = JwtHelper.encode(new ObjectMapper().writeValueAsString(tokenMap), new RsaSigner(aPrivate));
// 取出jwt令牌
String token = jwt.getEncoded();
System.out.println(token);
}
@Test
public void testVerify() {
//jwt令牌
String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1hbG9uZyIsImlkIjoiMTIzIn0.X68eLdVW69xJQGcICVZapWUay-vs5Fkv0cYRQoymuagR95G4bOkJ4d3rer6ko9gMS55htLmAmv3HkzeCOOE4R6fXJcGzyuDWY9D9lC7ca0-AX4okS4iLeTQAf53AIDLM3d1DQbRdJYrdDbSwhXZXIaaaaQNiVpnN3kGXK6YB7f1ohlEURFT50bf7lKVyc8xoJX4-ojLfZiWP2C8Ov84yEOc-Q2t9kRwPtPqQjial69b0FmPqtdfPbhJG66NAQdikRCxqHKSgb6QSMc9AF9AV4RluaPixoGIpufouWpWXyk9PvK-QJwwUJgM11emDTX2wv8lf5VXH-Wdg_1Jc_uwjLA";
String publickey = "-----BEGIN PUBLIC KEY-----
" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlym6EoAWxPYpTQHuWfqn
" +
"WqRkiUDrD+CyXM3Rzgtfi6ofudPDyEY5JbFcKIcfwjdQo+VLF0dIvyJA2csHhQIS
" +
"vZBspsE9cjz24GOuYSYufYtwOVcbjsvZ3JQaJahR6rBXzPTjvaiu1KtFtEJavWQ8
" +
"S9nhZ4MEMImW8r+Qphd0R4OB8KHrWoztYNznWpSaaH3QB446tQyyBre7tEPp3E6J
" +
"Xq+ApI5UKBpTdHW7b7MSJW836sYm8g5XfaXF667rlkdS03q3B182hUcghRY+fAyS
" +
"OtD2A0ib7XlrNADPL9X3dazWCYTnbNDswiqfpoDrbLy0PWFeIq09amL+E1ivKJZS
" +
"9wIDAQAB
" +
"-----END PUBLIC KEY-----";
//校验jwt
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
//获取jwt原始内容
String claims = jwt.getClaims();
System.out.println(claims);
}
@Test
public void getEnPassword(){
String encode = new BCryptPasswordEncoder().encode("123456");
System.out.println(encode);
}
}
这里别忘记了生成的JWT公钥私钥文件 放到reources目录下即可
搭建资源项目 项目目录
pom.xml
<?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 https://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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.weige</groupId>
<artifactId>java-skill-point</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>java-skill-point</name>
<description>java-skill-point</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<!--Springboot项目自带 -->
<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>
<!--Springboot Web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!--Mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis 用于接口注解快速开发(@Insert @Select @Update) -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--用于扫描mybatis注解 @Mapper @MapperScan-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>o
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8084
spring:
# mysql连接信息
datasource:
# mysql8之后
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: Weikai19991015.
security:
oauth2:
resource:
jwt:
key-uri: http://localhost:8888/oauth/token_key
Config文件
package com.weige.javaskillpoint.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class Config {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
JwtConfig文件
package com.weige.javaskillpoint.config;
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.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
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 org.springframework.util.FileCopyUtils;
import java.io.IOException;
@Configuration
public class JwtConfig {
public static final String public_cert = "public.key";
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
@Qualifier("tokenStore")
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
protected JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource(public_cert);
String publicKey;
try {
publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
// 设置校验公钥
converter.setVerifierKey(publicKey);
// 设置证书签名密码,否则报错
converter.setSigningKey("kaikeba");
return converter;
}
}
ResourceServerConfiguration文件
package com.weige.javaskillpoint.config;
import org.springframework.beans.factory.annotation.Autowired;
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.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/user/**")
.permitAll()
.antMatchers("/book/**").hasRole("admin")
.antMatchers("/**")
.authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore);
}
}
HelloController文件
package com.weige.javaskillpoint.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
@RestController
@RequestMapping("/cms")
public class HelloController {
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication) {
return authentication;
}
@GetMapping("/index/user")
@PreAuthorize("hasRole('ROLE_USER')")
public String index() {
return "index/user";
}
@GetMapping("/index/admin")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String index2() {
return "index/admin";
}
}
JavaSkillPointApplication文件
package com.weige.javaskillpoint;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.weige.javaskillpoint.dao")
public class JavaSkillPointApplication {
public static void main(String[] args) {
SpringApplication.run(JavaSkillPointApplication.class, args);
}
}
这里别忘记了生成的JWT公钥文件 放到reources目录下即可
两个项目都创建完成,在启动项目时。要注意先后顺序,先启动授权项目,在启动资源项目。因为资源项目会请求授权项目中的token中JTW对应的公钥解析是否对应私钥。
3.3 授权接口调用并测试资源接口
密码模式:
申请令牌 PostMan请求 localhost:8888/oauth/token?grant_type=password&username=user&password=123456&scope=read
资源项目接口测试:
资源服务器接口调用 USER http://localhost:8084/cms/index/user
资源服务器接口调用 ADMIN http://localhost:8084/cms/index/admin
资源服务器接口调用 ADMIN http://localhost:8084/cms/index/admin
3.4 资源项目中添加用户登录接口 获取令牌(token)返回给前端
资源项目添加几个类:
AdminConstants文件
package com.weige.javaskillpoint.constant;
public interface AdminConstants {
/**
* 请求头key
*/
String AUTHORIZATION_KEY = "Authorization";
/**
* Basic
*/
String BASIC_KEY = "Basic ";
/**
* 用户名key
*/
String USERNAME_KEY = "username";
/**
* 密码key
*/
String PASSWORD_KEY = "password";
/**
* 认证类型key
*/
String GRANT_TYPE_KEY = "grant_type";
/**
* 授权范围key
*/
String SCOPE_KEY = "scope";
/**
* 分隔符
*/
String SPLIT = ":";
}
LoginController文件
package com.weige.javaskillpoint.controller;
import com.weige.javaskillpoint.entity.LoginRequestDTO;
import com.weige.javaskillpoint.service.LoginService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class LoginController {
@Resource
private LoginService loginService;
/**
* 用户登录
*
* @param loginRequestDTO 登录请求DTO
* @return org.springframework.http.ResponseEntity
*/
@PostMapping("/login")
public ResponseEntity<OAuth2AccessToken> login(@RequestBody LoginRequestDTO loginRequestDTO) {
return loginService.login(loginRequestDTO);
}
}
LoginRequestDTO文件
package com.weige.javaskillpoint.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
public class LoginRequestDTO {
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
}
LoginService文件
package com.weige.javaskillpoint.service;
import com.weige.javaskillpoint.entity.LoginRequestDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
public interface LoginService {
/**
* 用户登录
*
* @param loginRequestDTO 登录请求DTO
* @return org.springframework.http.ResponseEntity
*/
ResponseEntity<OAuth2AccessToken> login(LoginRequestDTO loginRequestDTO);
}
LoginServiceImpl文件
package com.weige.javaskillpoint.service.impl;
import com.weige.javaskillpoint.constant.AdminConstants;
import com.weige.javaskillpoint.entity.LoginRequestDTO;
import com.weige.javaskillpoint.service.LoginService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Base64;
import java.util.Collections;
@Service
public class LoginServiceImpl implements LoginService {
@Resource
private RestTemplate restTemplate;
@Resource
private OAuth2ClientProperties oAuth2ClientProperties;
@Resource
private OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;
/**
* 用户登录
*
* @param loginRequestDTO 登录请求DTO
* @return org.springframework.http.ResponseEntity
*/
@Override
public ResponseEntity<OAuth2AccessToken> login(LoginRequestDTO loginRequestDTO) {
HttpHeaders headers = new HttpHeaders();
headers.set(AdminConstants.AUTHORIZATION_KEY, StringUtils.join(AdminConstants.BASIC_KEY, Base64.getEncoder().encodeToString(StringUtils.join(oAuth2ClientProperties.getClientId(), AdminConstants.SPLIT, oAuth2ClientProperties.getClientSecret()).getBytes())));
// 组装请求参数
MultiValueMap<String, String> map = new LinkedMultiValueMap<>(4);
map.put(AdminConstants.USERNAME_KEY, Collections.singletonList(loginRequestDTO.getUsername()));
map.put(AdminConstants.PASSWORD_KEY, Collections.singletonList(loginRequestDTO.getPassword()));
map.put(AdminConstants.GRANT_TYPE_KEY, Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));
map.put(AdminConstants.SCOPE_KEY, oAuth2ProtectedResourceDetails.getScope());
// 请求到授权服务器,将授权完的用户信息存到授权服务器,并申请令牌
return restTemplate.exchange(oAuth2ProtectedResourceDetails.getAccessTokenUri(), HttpMethod.POST, new HttpEntity(map, headers), OAuth2AccessToken.class);
}
}
application.yml文件
server:
port: 8084
spring:
# mysql连接信息
datasource:
# mysql8之后
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: Weikai19991015.
security:
oauth2:
resource:
jwt:
key-uri: http://localhost:8888/oauth/token_key
client:
access-token-uri: http://localhost:8888/oauth/token #令牌端点
user-authorization-uri: http://localhost:8888/oauth/authorize #授权端点
client-id: cms
client-secret: 123456
grant-type: password
scope: read,write
调用资源服务登录接口:PostMan调用 http://localhost:8084/user/login
拿到获取的token访问资源服务的接口 http://localhost:8084/cms/index/user
用admin登录
拿到获取的token访问资源服务的接口 http://localhost:8084/cms/index/admin
完结撒花 !