SpringSecurity与JWT(笔记)

一、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的优点

  1. 支持跨域访问,Cookie不支持跨域访问
  2. 无状态
  3. 更适用CDN
  4. 去耦合,不要一个特定的身份
  5. 更适用于移动应用。因为它们不支持Cookie
  6. 不再依赖Cookie,因此不用考虑CSRF
  7. 性能更好,因为只是进行一次Token验证和解析
  8. 不需要为页面做特俗处理
  9. JSON Web Token 标准已经存在多个后端库与多家公司支持

1.2 JWT

JSON Web Token 开放的行业标准,用于在通信双方传递JSON对象,传递的信息经过数字签名,是可以被验证和信任。
JWT官网
JWT标准

JWT令牌的优点

  1. jwt基于JSON,非常方便解析
  2. 可以在令牌中自定义丰富内容,易扩展
  3. 通过非对称加密算法以及数字签名技术,JWT防止篡改,安全性高
  4. 资源服务使用JWT可不以来认证服务即可完成授权
    缺点
  5. 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)

签证信息,签证信息由三部分组成:

  1. header(base64后的)
  2. payload(base64后的)
  3. 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服务器
在这里插入图片描述

登陆后,返回到了客户端,并且,获取到了登陆信息和秘钥
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值