使用JWT替换默认令牌token

JSON Web Token (JWT)

自包含

token的创建

org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)

在这里插入图片描述

总结

可见token根据UUID生成的
所以token里面的信息是无意义的
看上节redis里面token的存储,是把相关信息与token做了关联
如过redis服务器挂掉了,那么这个token就是毫无意义的,以为他本身不包含任何信息

jwt 恰恰相反,它是自包含了相关的信息

密签

密签的意思 不是 jwt 信息的加密或解密

他是指如果 jwt 里面的信息被修改以后 我们能够通过密签发现

jwt不能存储敏感信息

可扩展

可以在里面放任何数据

替换默认token

TokenStoreConfig

这个配置里面配置了两个TokenStore
根据不同的配置确认token的类型
默认是JwtTokenStore

@Configuration
public class TokenStoreConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "redis",matchIfMissing = false)
    public TokenStore tokenStore(){
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Configuration
    @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true)
    //prefix 是只application配置文件中 配置的最后一个点分隔后前面的一部分 这叫前缀
    //name  对应的是后缀 最后一个点分隔后的后面的那部分
    //即检查的配置项为 whale.security.oauth2.storeType
    //havingValue = "jwt"  当配置项的值为jwt的时候 这个类里面的所有配置生效
    //matchIfMissing 如果配置文件里没有找到这个属性,即默认生效
    public static class JwtTokenConfig{

        @Autowired
        private SecurityProperties securityProperties;

        //token 的存储
        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        //token生成处理
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
            return  jwtAccessTokenConverter;
        }
    }

}

WhaleAuthenticationServiceConfig

在这里插入图片描述

OAuth2Properties

/**
     * 这个是秘钥 一定要保存好
     *
     * 发出去的令牌要用它签名
     * 收到的令牌也要用它来验签
     *
     * jwt 是token的标准协议,唯一的安全性就是这个秘钥
     *
     */
    private String jwtSigningKey = "whale";

测试 jwt

用表单登录获取token的方式
发现 这次的 access_token 很长
在这里插入图片描述

"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTg2MDg5NjQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiMzYzNjJiZWUtMjFjOS00ZWM3LWIxMmUtYjQyYzYyYzViZjI5IiwiY2xpZW50X2lkIjoid2hhbGUiLCJzY29wZSI6WyJhbGwiLCJyZWFkIiwid3JpdGUiXX0.PJ1vcPCccfVfrHxCJzRs19xoFV6hnu5lMetJuYARylw",

https://jwt.io/ jwt解析
在这里插入图片描述访问资源成功
在这里插入图片描述

jwt token加额外信息

实现TokenEnhancer

在这里插入图片描述

WhaleTokenEnhancer

public class WhaleTokenEnhancer implements TokenEnhancer {
    /**
     *
     * @param oAuth2AccessToken
     * @param oAuth2Authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {

        Map<String,Object> info = new HashMap<>();
        info.put("company","埃森哲");

        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);

        return oAuth2AccessToken;
    }
}

TokenStoreConfig 配置 TokenEnhancer bean

·······························
  public static class JwtTokenConfig{

    		·····················
        @Bean
        @ConditionalOnMissingBean(name = "jwtTokenEnhancer")
        public TokenEnhancer jwtTokenEnhancer(){
            return  new WhaleTokenEnhancer();
        }
    }

WhaleAuthenticationServiceConfig 中配置tokenEnhancerChain

部分代码如下


    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    /**
     *
     * @param endpoints  oauth/token 的入口点
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        super.configure(endpoints);
//        endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
        endpoints.tokenStore(tokenStore)
                 .authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);

        if(jwtAccessTokenConverter!=null && jwtTokenEnhancer!=null){

            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();

            List<TokenEnhancer> enhancers = new ArrayList<>();
            enhancers.add(jwtTokenEnhancer);
            enhancers.add(jwtAccessTokenConverter);

            tokenEnhancerChain.setTokenEnhancers(enhancers);

            endpoints.tokenEnhancer(tokenEnhancerChain)
                     .accessTokenConverter(jwtAccessTokenConverter);
        }
    }

测试

在这里插入图片描述eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCIsInJlYWQiLCJ3cml0ZSJdLCJjb21wYW55Ijoi5ZD5qOu5ZOyIiwiZXhwIjoxNTU4NjEzNjQzLCJhdXRob3JpdGllcyI6WyJhZG1pbiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI5OTA3NWZjOC04MTI3LTQ2YWYtYTM2OS0wNTlkODlkNTIwMTEiLCJjbGllbnRfaWQiOiJ3aGFsZSJ9.1Ild0mbgxQn1No0NQIQI2gWrTDFDcgdHYHdC_kdGq_w

解析如下

在这里插入图片描述

Authentication解析额外信息?

注意 虽然我们的jwt可以携带额外信息
但是 我们的spring 只是 将 jwt规范里面的信息解析成 Authentication
在这里插入图片描述
如果我们需要jwt token 里的额外信息呢?

为此我们需要加入新的依赖

https://jwt.io/
https://www.jsonwebtoken.io/

demo pom 文件中加依赖

试了几个 0.9.0 版本可以

 <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
解析额外信息 UserController
    @GetMapping("/me11")
    public Object getCurrentUser11(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
        //spring 会自动找到Authentication类型的数据注入
        String header = request.getHeader("Authorization");
        String token = StringUtils.substringAfter(header, "Bearer ");

        //需要用秘钥来验证签名
        //注意 签名的时候 默认用的是秘钥的utf-8编码 ,同时验签的时候也要知道 编码 utf-8
        Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("utf-8"))
                .parseClaimsJws(token).getBody();

        String company = (String) claims.get("company");

        System.out.println(company);
        return  authentication;
    }
测试

在这里插入图片描述
在这里插入图片描述

token令牌的刷新

refresh_token

在用户无感的情况下用refresh_token得到一个新的token
在这里插入图片描述

测试

但我的refresh_token没有测试成功
也不知道什么原因

在这里插入图片描述

按道理是完全可以的,也不知道问题出在哪

https://auth0.com/docs/tokens/refresh-token/current#9e51c5c22d4b450cab209018dc3be588_java

Use a Refresh Token
To exchange the Refresh Token you received during authorization for a new Access Token, make a POST request to the /oauth/token endpoint in the Authentication API, using grant_type=refresh_token.

curl --request POST \
  --url 'https://YOUR_DOMAIN/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=refresh_token&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&refresh_token=YOUR_REFRESH_TOKEN'

https://openapi.baidu.com/wiki/index.php?title=使用Refresh_Token获取Access_Token

https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

https://auth0.com/docs/tokens/refresh-token/current

https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#acquiring--client-ids-and-secrets

### 使用Sa-Token 替代 JWT 实现身份验证 #### 添加依赖项 为了在 Spring Boot 项目中集成 Sa-Token 进行身份验证,需先引入必要的 Maven 或 Gradle 依赖。对于 Maven 用户,在 `pom.xml` 文件中加入如下配置[^2]: ```xml <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.27.0</version> </dependency> ``` #### 初始化配置类 接着定义一个 Java 类来初始化 Sa-Token 的基本参数。通常情况下,这一步骤可以通过创建名为 `SaTokenConfig.java` 的文件完成。 ```java import cn.dev33.satoken.SaManager; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class SaTokenConfig { @Bean public void init() { // 设置 token 名称为 satoken SaManager.setTokenName("satoken"); // 可选:设置 Token 超时时间,默认是半小时(毫秒), 此处设为一天 SaManager.setTimeout(1000 * 60 * 60 * 24); } } ``` #### 创建控制器处理登录请求 编写 RESTful API 接口用于接收客户端发送过来的用户名密码,并通过调用服务层的方法来进行用户校验。如果验证成功,则返回给前端带有新生成令牌的信息;反之则给出错误提示信息。 ```java @RestController @RequestMapping("/auth") public class AuthController { private final UserService userService; @Autowired public AuthController(UserService userService) { this.userService = userService; } @PostMapping("/login") public ResponseEntity<Map<String, Object>> login(@RequestBody UserLoginRequest request){ try{ String username = request.getUsername(); String password = request.getPassword(); boolean isValidUser = userService.checkCredentials(username,password); if(!isValidUser){ throw new RuntimeException("Invalid credentials."); }else{ StpUtil.login(userService.getUserIdByUsername(username)); Map<String,Object> resultMap=new HashMap<>(); resultMap.put("token",StpUtil.getTokenValue()); return ResponseEntity.ok(resultMap); } }catch(Exception e){ log.error(e.getMessage(),e); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); } } } ``` 上述代码片段展示了如何利用 `StpUtil.login()` 方法建立新的会话以及获取当前用户的唯一标识符(即 LoginId),并通过 `getTokenValue()` 获取对应的访问令牌[^3]。 #### 安全拦截器设定 为了让应用程序能够自动解析 HTTP 请求头中的 Bearer Tokens 并将其绑定至当前线程上下文中以便后续业务逻辑可以方便地读取到已认证的身份信息,还需要注册自定义的安全过滤器。 ```java @Configuration public class WebSecurityConfig extends WebMvcConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(new SaServletFilter(), UsernamePasswordAuthenticationFilter.class); } } ``` 以上就是基于 Sa-Token 构建安全框架的主要步骤概述。值得注意的是,具体实现细节可能会因实际需求而有所不同,因此建议开发者仔细阅读官方文档以获得最准确的帮助和支持[^1]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值