社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
技术框架:springboot+oauth2+springsecurity+mybatis-plus+mysql+redis
主要是为了学习oauth2而写的demo,主要用到了oauth2的password模式
其实用了jwt就不应该用redis,因为jwt是无状态的,但是没办法啊,比如用户退出或者用户修改密码,导致原有的token有效,服务端无法控制这些token,所以就加了redis,只会贴部分代码,项目地址在最下面
解决了关于oauth2抛出的异常被自定义异常捕获,实现统一的格式返回
springboot 版本 2.1.3.RELEASE
oauth2 版本 2.3.5.RELEASE
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
首先项目结构:
pom
<?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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.pwl</groupId>
<artifactId>springboot-oath-pwl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-oath-pwl</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--HikariCP连接池-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SecurityConfiguration配置
package com.pwl.oath.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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
/*@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers().antMatchers("/order/*").anyRequest()
.and()
.authorizeRequests()
.antMatchers("/order/*").authenticated()
.and().formLogin().and()
.httpBasic();
// @formatter:on
}*/
/**
* @Date 14:35 2019/7/11
* @Param [不用加密]
* @return org.springframework.security.crypto.password.PasswordEncoder
**/
@Bean
public static PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
资源的配置OAuth2ServerConfig
package com.pwl.oath.config;
import com.pwl.oath.exception.AuthExceptionEntryPoint;
import com.pwl.oath.exception.BootOAuth2WebResponseExceptionTranslator;
import com.pwl.oath.exception.CustomAccessDeniedHandler;
import com.pwl.oath.oath.UserVoDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
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.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
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 org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class OAuth2ServerConfig {
private static final String DEMO_RESOURCE_ID = "order";
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true)
//自定义Token异常信息,用于token校验失败返回信息
.authenticationEntryPoint(new AuthExceptionEntryPoint())
//授权异常处理
.accessDeniedHandler(new CustomAccessDeniedHandler());
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests().antMatchers("/oauth/**").permitAll()
.and()
.authorizeRequests().antMatchers("/sysUser/**").authenticated()
.and()
//验证角色自动会加ROLE_前缀,以下需要用户有ROLE_USER 角色才能访问
.authorizeRequests().antMatchers("/hello/**").hasRole("USER")
.and().
authorizeRequests().anyRequest().authenticated();
// @formatter:on
}
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置客户端
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret("123456");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints
.tokenEnhancer(tokenEnhancerChain)
.accessTokenConverter(accessTokenConverter())
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
// 2018-4-3 增加配置,允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
endpoints.reuseRefreshTokens(true);
//oauth2登录异常处理
endpoints.exceptionTranslator(new BootOAuth2WebResponseExceptionTranslator());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
/**
* @Author Pan Weilong
* @Description jwt加密秘钥
* @Date 17:58 2019/7/10
**/
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("asdfadf");
return converter;
}
/**
* @Author Pan Weilong
* @Description 使用redis保存生成的token
* @Date 14:38 2019/7/11
* @Param []
* @return org.springframework.security.oauth2.provider.token.TokenStore
**/
@Bean
public TokenStore tokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
//key前缀
tokenStore.setPrefix("pwl_");
//new JwtTokenStore(accessTokenConverter())
return tokenStore;
}
/**
* jwt 生成token 定制化处理
* @return TokenEnhancer
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
UserVoDetail userDto = (UserVoDetail) authentication.getUserAuthentication().getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>(1);
additionalInfo.put("license", "pwl");
additionalInfo.put("userId" , userDto.getUserId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
//设置token的过期时间30分钟
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE, 30);
((DefaultOAuth2AccessToken) accessToken).setExpiration(nowTime.getTime());
return accessToken;
};
}
}
}
自定义实现UserDetailsService
package com.pwl.oath.oath;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.pwl.oath.entity.SysUser;
import com.pwl.oath.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
* @author Pan Weilong
* @date 2019/7/9 15:57
* @description: 接口.
*/
@Component("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Wrapper<SysUser> query = new EntityWrapper<>();
query.eq("user_name",s);
SysUser sysUser = sysUserService.selectOne(query);
if(sysUser==null){
throw new UsernameNotFoundException("用户不存在");
}
return new UserVoDetail(sysUser.getUserId(),sysUser.getUserName(),sysUser.getPassword());
}
}
用户信息实现UserDetails接口
package com.pwl.oath.oath;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Pan Weilong
* @date 2019/7/9 16:02
* @description: 接口.
*/
public class UserVoDetail implements UserDetails {
private Long userId;
private String username;
private String password;
public UserVoDetail(Long userId, String username, String password) {
this.userId = userId;
this.username = username;
this.password = password;
}
//加了一个ROLE_USE角色
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USE"));
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
上面重写了loadUserByUsername,登录的时候根据用户名查询数据库用户是否存在,至于什么时候调用的
可以看这个认证流程
解决oauth2抛异常实现统一的格式
捕获Token异常信息,用于tokan校验失败返回信息,比如token过期/验证错误,jwt错误
package com.pwl.oath.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author Pan Weilong
* @date 2019/7/10 11:27
* @description: 自定义Token异常信息,用于tokan校验失败返回信息,比如token过期/验证错误
*/
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws ServletException {
Map map = new HashMap();
map.put("code", "401");
map.put("message", authException.getMessage());
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}
登录抛异常 比如用户或者密码/scope报错接住的异常
package com.pwl.oath.exception;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import java.io.IOException;
/**
* @author Pan Weilong
* @date 2019/7/10 9:39
* @description: 登录抛异常 比如用户或者密码/scope报错接住的异常
*/
public class BootOAuth2WebResponseExceptionTranslator implements WebResponseExceptionTranslator{
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
// Try to extract a SpringSecurityException from the stacktrace
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
// 异常栈获取 OAuth2Exception 异常
Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
// 异常栈中有OAuth2Exception
if (ase != null) {
return handleOAuth2Exception((OAuth2Exception) ase);
}
ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,causeChain);
if (ase != null) {
return handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e));
}
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (ase instanceof AccessDeniedException) {
return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
}
ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
if (ase instanceof HttpRequestMethodNotSupportedException) {
return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
}
// 不包含上述异常则服务器内部错误
return handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
}
private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
int status = e.getHttpErrorCode();
HttpHeaders headers = new HttpHeaders();
headers.set("Cache-Control", "no-store");
headers.set("Pragma", "no-cache");
if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
}
String message=e.getMessage();
BootOAuth2Exception exception = new BootOAuth2Exception(message,e);
ResponseEntity<OAuth2Exception> response = new ResponseEntity<OAuth2Exception>(exception, headers,HttpStatus.valueOf(status));
return response;
}
private static class UnauthorizedException extends OAuth2Exception {
public UnauthorizedException(String msg, Throwable t) {
super(msg, t);
}
@Override
public String getOAuth2ErrorCode() {
return "unauthorized";
}
@Override
public int getHttpErrorCode() {
return 401;
}
}
private static class ServerErrorException extends OAuth2Exception {
public ServerErrorException(String msg, Throwable t) {
super(msg, t);
}
@Override
public String getOAuth2ErrorCode() {
return "server_error";
}
@Override
public int getHttpErrorCode() {
return 500;
}
}
private static class ForbiddenException extends OAuth2Exception {
public ForbiddenException(String msg, Throwable t) {
super(msg, t);
}
@Override
public String getOAuth2ErrorCode() {
return "access_denied";
}
@Override
public int getHttpErrorCode() {
return 403;
}
}
}
package com.pwl.oath.exception;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
* @author Pan Weilong
* @date 2019/7/10 10:09
* @description: 接口.
*/
@JsonSerialize(using = BootOAuthExceptionJacksonSerializer.class)
public class BootOAuth2Exception extends OAuth2Exception {
public BootOAuth2Exception(String msg, Throwable t) {
super(msg, t);
}
public BootOAuth2Exception(String msg) {
super(msg);
}
}
package com.pwl.oath.exception;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Map;
/**
* @author Pan Weilong
* @date 2019/7/10 10:10
* @description: 接口.
*/
public class BootOAuthExceptionJacksonSerializer extends StdSerializer<BootOAuth2Exception> {
protected BootOAuthExceptionJacksonSerializer() {
super(BootOAuth2Exception.class);
}
@Override
public void serialize(BootOAuth2Exception value, JsonGenerator jgen, SerializerProvider serializerProvider) throws IOException {
jgen.writeStartObject();
jgen.writeObjectField("code", value.getHttpErrorCode());
jgen.writeStringField("msg", value.getMessage());
if (value.getAdditionalInformation()!=null) {
for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
String key = entry.getKey();
String add = entry.getValue();
jgen.writeStringField(key, add);
}
}
jgen.writeEndObject();
}
}
授权失败的异常
package com.pwl.oath.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Pan Weilong
* @date 2019/7/10 15:49
* @description: 授权失败处理异常
*/
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
Map map = new HashMap();
map.put("code", "403");
map.put("message", "授权失败,禁止访问");
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
上面的这些异常需要在配置文件里面配置
上面的OAuth2ServerConfig已经配置上面的异常处理
获取token,这是我本地的,参数字段必须传
下面是post请求:
localhost:8080/oauth/token?username=user1&password=123456&grant_type=password&scope=select&client_id=client_1&client_secret=123456
访问必须登录的接口/sysUser/**
package com.pwl.oath.controller;
import com.pwl.oath.common.ResultVO;
import com.pwl.oath.entity.SysUser;
import com.pwl.oath.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author pwl
* @since 2019-06-20
*/
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@GetMapping
public ResultVO getUserList(){
List<SysUser> sysUsers = sysUserService.selectList(null);
return new ResultVO(sysUsers);
}
@GetMapping("/getList")
public ResultVO getList(){
List<SysUser> sysUsers = sysUserService.selectList(null);
return new ResultVO(sysUsers);
}
}
必须登录了才能访问
输入错误的token,会被拦截
登录的时候密码输错了,会被异常拦截
刷新token
post方式 refresh_token是刷新token grant_type必须是refresh_token,参数不能少
header里面不要忘了加上Authorization
127.0.0.1:8080/oauth/token?refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsaWNlbnNlIjoicHdsIiwiYXVkIjpbIm9yZGVyIl0sInVzZXJfbmFtZSI6InVzZXIxIiwic2NvcGUiOlsic2VsZWN0Il0sImF0aSI6IjFiMTY5ZjFmLWNmNjEtNDYzMS05Y2Y0LWVlMmNjZmQyZDUyNiIsImV4cCI6MTU2NTQyMDE2MSwidXNlcklkIjo0LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRSJdLCJqdGkiOiI1M2Q3Yzg1MC1lOTM0LTQxYjktOTc4NS1kMzIyYmM3MWY2NmQiLCJjbGllbnRfaWQiOiJjbGllbnRfMSJ9.iOZwFRxJn5BxmBD3zypLvLIONX7aakD_ZPGwCAX_dCY&grant_type=refresh_token&client_id=client_1&scope=select&client_secret=123456
会获取一个新的token,以前的token就不能使用了
访问需要USER角色的接口,会被拦截,因为目前用户只具备ROLE_USE角色(因为上面写死了)
最后就是退出登录会清掉redis中的token,所以下次拿着这个token就不能访问了,已经失效了
存放在redis中的token
每次用户退出系统/注销系统/修改密码操作后,前端调一下这个接口就可以了,以前的token 就失效了
项目地址:https://github.com/James-Pan0525/springboot-oath-pwl.git
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!