文章目录
一、JWT
1.1 常见的认证机制
- HTTP Basic Auth
可以理解为每次请求API时都提供用户的username和password,简言之时配合RESTful API使用的最简单的认证方式,只需提供用户名密码即可。但又把用户名密码暴露给第三方用户的可能,因此会避免使用HTTPBasicAuth的模式。 - Cookie Auth
就是为一次请求在服务器端创建一个Session对象,同时在客户端创建一个Cookie对象;通过客户端带上来的Cookie对象与服务器端的Session对象来匹配实现状态管理。默认的,当我们关闭浏览器的时候,cookie会被删除,当我们可以修改cookie的expire time使得cookie在一定时间内有效。 - OAuth
开放的授权标准,允许用户让第三方应用访问该用户在某一个服务上存储的私密资源,无需将用户名和密码提供给第三方应用。
但是太过复杂 - Token Auth
使用基于token的身份验证方法,在服务端不需要存储用户的登陆记录。服务端签发一个Token后,有客户端存储,服务器每次只需要验证客户端请求的Token来识别用户身份。
Token Auth的优点
- 支持跨域访问,Cookie不支持跨域访问
- 无状态
- 更适用CDN
- 去耦合,不要一个特定的身份
- 更适用于移动应用。因为它们不支持Cookie
- 不再依赖Cookie,因此不用考虑CSRF
- 性能更好,因为只是进行一次Token验证和解析
- 不需要为页面做特俗处理
- JSON Web Token 标准已经存在多个后端库与多家公司支持
1.2 JWT
JSON Web Token 开放的行业标准,用于在通信双方传递JSON对象,传递的信息经过数字签名,是可以被验证和信任。
JWT官网
JWT标准
JWT令牌的优点
- jwt基于JSON,非常方便解析
- 可以在令牌中自定义丰富内容,易扩展
- 通过非对称加密算法以及数字签名技术,JWT防止篡改,安全性高
- 资源服务使用JWT可不以来认证服务即可完成授权
缺点 - JWT令牌长,占用存储空间大
1.3 JWT组成
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷、签名
1.3.1 头部(Header)
头部用于描述该JWT的最基本的信息,例如其类型以及签名所用的算法。
{
"alg":"HS256",
"typ":"JWT"
}
- typ:是类型
- alg:签名的算法。使用的算法是HS256算法
我们对头部的JSON字符串进行BASE64编码
Base64:
- 是一中基于64个可打印字符来表示二进制数据的表示方法。由于 2 6 = 64 2^{6}=64 26=64 ,因此6bit为一个单元,对因为某个可打印字符。
- 3 Byte = 24 bit=4 Base64单元
- JDK提供了Base64Encoder和BASE64Decoder,用他们快速的解码和编码
1.3.2 负载(Payload)
存放有效信息的地方
- 标准中注册的声明
iss:jwt的签发者
sub:jwt所面向的用户
aud:接受jwt的一方
exp:jwt的过期时间,这个过期时间必须大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的
iat:jwt的签发时间
jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
-
公共的声明
可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要的信息,但不建议添加敏感信息,因为该部分在客户端可解密 -
私有的声明
私有声明是提供者和消费者共同定义的声明,一般不建议存放敏感信息。因为是Base64是对称解密的,意味着该部分信息可以归类为明文信息。
这个地方写的就是自定义的claim。标准规定的Claim,接受方拿到后知道怎么验证和处理。单私有的声明不会被验证,除非明确告诉接收方要对这些声明进行验证以及规则才行。
该部分也用base64进行编码,得到JWT的第二部分
1.3.3 签证、签名(signature)
签证信息,签证信息由三部分组成:
- header(base64后的)
- payload(base64后的)
- secret(盐,一定要保密)
将header与payload加密后的字符串连接,通过header中声明的加密方式进行加盐secret组合的方式加密,然后就构成了jwt的第三部分
再次将这三个部分用‘.’连接,jiushi JWT
secret是保存在服务端的,jwt的签也是发生在服务器端的,secret就是用来进行jwt的前方和jwt的验证,即私钥。
1.4 jjwt快速开始
jjwt依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.6.5</version>
</parent>
<groupId>org.example</groupId>
<artifactId>jwttest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
签发
@Test
public void testCreateToken(){
long exp = System.currentTimeMillis() + 60*1000; //单位毫秒
JwtBuilder jwtBuilder = Jwts.builder()
//标识{"jti":"8888"}
.setId("8888")
//接收的客户
.setSubject("customer")
//创建日期
.setIssuedAt(new Date())
//签名方式、盐
.signWith(SignatureAlgorithm.HS256,"xxxx")
//过期时间
.setExpiration(new Date(exp));
String token = jwtBuilder.compact();
System.out.println(token);
}
同时我们会发现,每次运行,结果都不一样,因为有签发时间的加入。
解码
@Test
public void testParseToken(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiY3VzdG9tZXIiLCJpYXQiOjE2NjI0NTU2NTUsImV4cCI6MTY2MjQ1NTcxNX0.FnRSFfz9f-r8sg6Vpq5YGzzD7QRGxE04ReJLpql4jT0";
Claims claims = Jwts.parser()
.setSigningKey("xxxx")
.parseClaimsJws(token)
.getBody();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
System.out.println("签发时间:" + simpleDateFormat.format(claims.getIssuedAt()));
System.out.println("过期时间:" + simpleDateFormat.format(claims.getExpiration()));
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
}
失效会抛出异常
1.5 自定义声明
@Test
public void testCreateClaim(){
System.out.println(
Jwts.builder()
.setId("8888")
.setSubject("test")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256,"xxxx")
.claim("roles","admin")
.claim("age","10").compact()
);
}
@Test
public void testParseClaim(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoidGVzdCIsImlhdCI6MTY2MjQ1NjA4OSwicm9sZXMiOiJhZG1pbiIsImFnZSI6IjEwIn0.aTOxpAbAEg1aO5DtyzfT1MBfTMunNS1rm96QM2UxlyA";
Claims claims = Jwts.parser()
.setSigningKey("xxxx")
.parseClaimsJws(token)
.getBody();
System.out.println("roles:" + claims.get("roles"));
System.out.println("roles:" + claims.get("age"));
}
二、SpringSecurityOauth2整合JWT
2.1 整合密码授权
<properties>
<spring-cloud.version>2021.1</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!--SpringCloud依赖管理工具-->
<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>
<dependencies>
<!-- spring security组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<!-- ouath2组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
我们整合密码认证
验证逻辑
package com.yjx23332.jjwtdemo.service.Impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.authority.AuthorityUtils;
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.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Lazy
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1. 查询数据库判断用户名是否存在,不存在则抛出UsernameNotFoundException异常
if(!"admin".equals(username)){
throw new UsernameNotFoundException("用户名不存在");
}
//2. 把查询出来的密码(注册时已经加密过得密码)与传入密码比较。此处就不查了,直接赋值
String password = passwordEncoder.encode("123");
//使用的是Security的User
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
}
}
package com.yjx23332.jjwtdemo.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.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//表单提交
httpSecurity.formLogin()
.permitAll();
//授权认证
httpSecurity.authorizeRequests()
//放行所有oauth请求
.antMatchers("/oauth/**").permitAll()
//所有的请求都必须登陆之后,被访问
.anyRequest().authenticated();
//登出处理
httpSecurity.logout()
.permitAll();
//csrf防御关闭
httpSecurity.csrf().disable();
//允许跨域
httpSecurity.cors().disable();
}
@Bean
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
资源服务器
package com.yjx23332.jjwtdemo.config;
import org.springframework.context.annotation.Configuration;
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;
/**
* 资源服务器配置
* */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//所有资源都需要被认证
.anyRequest()
.authenticated()
.and()
//有权限的用户可访问
.requestMatchers()
.antMatchers("/user/**");
}
}
token存储配置
package com.yjx23332.jjwtdemo.config;
import org.apache.el.parser.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
/**
* JwtToken配置类
*
* */
@Configuration
public class JwtTokenStoreConfig {
@Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 用于oauth秘钥转换
* */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//配置JWT使用的秘钥
jwtAccessTokenConverter.setSigningKey("test_key");
return jwtAccessTokenConverter;
}
}
授权配置
package com.yjx23332.jjwtdemo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
@Configuration
@EnableAuthorizationServer
/**
* 授权服务器
* */
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
@Qualifier("jwtTokenStore")
private TokenStore tokenStore;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//配置Client-ID
.withClient("admin")
//配置Client-secret
.secret(passwordEncoder.encode("112233"))
//访问token有效期
.accessTokenValiditySeconds(3600)
//授权成功后跳转
.redirectUris("http://www.baidu.com")
//使用范围
.scopes("all")
//授权码模式
.authorizedGrantTypes("password");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
//配置令牌存储策略
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter);
}
}
可以看到,已经变为JWT格式的令牌
2.2 扩展JWT
package com.yjx23332.jjwtdemo.config;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
/**
* 内容增强
* */
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String,Object> info = new HashMap<>();
info.put("enhance","enhance info");
((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
return oAuth2AccessToken;
}
}
配置
package com.yjx23332.jjwtdemo.config;
import org.apache.el.parser.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
/**
* JwtToken配置类
*
* */
@Configuration
public class JwtTokenStoreConfig {
@Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 用于oauth秘钥转换
* */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//配置JWT使用的秘钥
jwtAccessTokenConverter.setSigningKey("test_key");
return jwtAccessTokenConverter;
}
@Bean
public JwtTokenEnhancer jwtTokenEnhancer(){
return new JwtTokenEnhancer();
}
}
授权服务器配置
package com.yjx23332.jjwtdemo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableAuthorizationServer
/**
* 授权服务器
* */
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
@Qualifier("jwtTokenStore")
private TokenStore tokenStore;
@Autowired
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//配置Client-ID
.withClient("admin")
//配置Client-secret
.secret(passwordEncoder.encode("112233"))
//访问token有效期
.accessTokenValiditySeconds(3600)
//授权成功后跳转
.redirectUris("http://www.baidu.com")
//使用范围
.scopes("all")
//授权模式
.authorizedGrantTypes("password");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> delegates = new ArrayList<>();
//加入
delegates.add(jwtTokenEnhancer);
//转换
delegates.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(delegates);
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
//配置令牌存储策略
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(enhancerChain);
}
}
2.3 资源管理器解析内容
package com.yjx23332.jjwtdemo.controller;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 获取当前用户
* */
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(HttpServletRequest request){
String head = request.getHeader("Authorization");
String token = head.substring(head.indexOf("bearer") + 7);
return Jwts.parser().setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();
}
}
2.4 刷新令牌
在授权服务器的密码模式后,加一个刷新令牌
//授权模式
.authorizedGrantTypes("password","refresh_token");
随后,我们修改请求参数
我们修改请求为刷新令牌模式,可以看到请求后,直接过去了相关信息
2.5 单点登陆
我们另开一个项目,开始写客户端服务
使用的是验证码模式
server:
port: 8080
servlet:
session:
# 防止Cookie冲突
cookie:
name: OAUTH2-CLIENT-SESSIONID01
# 授权服务器地址
oauth2-server-url: http://localhost:8081
security:
oauth2:
client:
client-id: admin
client-secret: 112233
user-authorization-uri: ${oauth2-server-url}/oauth/authorize
access-token-uri: ${oauth2-server-url}/oauth/token
resource:
jwt:
key-uri: ${oauth2-server-url}/oauth/token_key
package com.yjx23332.oauth2client01demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
@SpringBootApplication
//单点登录开启
@EnableOAuth2Sso
public class MainApplication {
public static void main(String[] args){
SpringApplication.run(MainApplication.class,args);
}
}
package com.yjx23332.oauth2client01demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getCurrentUser")
public Object getCurrentuser(Authentication authentication){
return authentication;
}
}
在授权服务器处添加和修改相关代码
package com.yjx23332.jjwtdemo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableAuthorizationServer
/**
* 授权服务器
* */
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
@Qualifier("jwtTokenStore")
private TokenStore tokenStore;
@Autowired
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//配置Client-ID
.withClient("admin")
//配置Client-secret
.secret(passwordEncoder.encode("112233"))
//访问token有效期
.accessTokenValiditySeconds(3600)
//刷新令牌的有效期
.refreshTokenValiditySeconds(864000)
//自动授权,就不用自己去选择是否授权
.autoApprove(true)
//授权成功后,跳转至回客户端的登陆页面
.redirectUris("http://localhost:8080/login")
//使用范围
.scopes("all")
//授权模式
.authorizedGrantTypes("password","refresh_token","authorization_code");
}
/**
* 重写单点认证的身份处理
* */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//获取秘钥需要身份验证,使用单点登录时必须配置
security.tokenKeyAccess("isAuthenticated()");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> delegates = new ArrayList<>();
//加入
delegates.add(jwtTokenEnhancer);
//转换
delegates.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(delegates);
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
//配置令牌存储策略
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(enhancerChain);
}
}
启动两个项目后,
访问
http://localhost:8080/user/getCurrentUser
发现成功转到8081服务器
登陆后,返回到了客户端,并且,获取到了登陆信息和秘钥