SpringSecurity Oauth2 - 07 整合Redis延长页面自动退出登录时间


在前面几篇文章中,梳理了 SpringSecurity Oauth2 的认证登录流程,我们知道认证成功后的token信息是存储在redis中,现在有一个需求:实现一个心跳机制,当用户使用账号密码登录系统后,如果10分钟内有请求访问系统,那么就延长系统退出登录的时间,如果10min内没有任何请求访问系统,那么10分钟后系统将退出登录。如何实现改需求呢?

根据前面文章的源码分析我们知道请求会先经过OAuth2AuthenticationProcessingFilter过滤器,该过滤器第一步就是提取token,如果token=null代表用户未认证,因此我们可以利用这一逻辑,实现一个自定义的 CustomTokenExtractor ,在请求进来的时候判断控制页面自动退出登录的 redis key是否存在,如果不存在说明已经到达系统自动退出登录的时间,返回给OAuth2AuthenticationProcessingFilter过滤器token=null,用户未认证信息。如果key存在就延长key的过期时间。

我们先来实现该功能,然后走读源码带大家看下整体流程。

1. 延长页面自动退出登录时间

1. 认证登录控制器 AuthController

@Slf4j
@RestController
@RequestMapping("/api/v1")
public class AuthController {

    @Autowired
    private LoginService loginService;

    @PostMapping("/login")
    public ApiResponse<AuthenticationInfo> authority( @Validated @RequestBody LoginQo loginQo) {
        AuthenticationInfo authenticationInfo = loginService.checkAndAuth( loginQo);
        return new ApiResponse<>(0,"success",authenticationInfo);
    }
}

2. 认证登录业务逻辑层 LoginServiceImpl

  • 校验登录密码是否正确;
  • 获取访问令牌access_token;
  • redis存储认证信息;
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Autowired
    private UserService userService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 登录认证
     * @param loginQo 登录请求体
     * @return AuthenticationInfo
     */
    @Override
    public AuthenticationInfo checkAndAuth(LoginQo loginQo) {
        // 登录认证获取访问令牌和用户信息
        AuthenticationInfo authenticationInfo = checkLogin(loginQo);

        // 处理redis中的认证数据
        processRedisAuthInfo(authenticationInfo);
        return authenticationInfo;
    }

    /**
     * 处理redis中的认证数据
     * @param authenticationInfo 认证成功后的令牌和用户信息
     */
    private void processRedisAuthInfo(AuthenticationInfo authenticationInfo) {
        AuthToken authToken = authenticationInfo.getAuthToken();
        String accessToken = authToken.getAccessToken();
        String refreshToken = authToken.getRefreshToken();
        User userInfo = authenticationInfo.getUser();

        // 用户登录后,页面自动退出登录的时间(分钟)
        int overTime = 10;
        // 页面自动退出登录的时间,单位毫秒
        long pageOverTimeLong = (long) overTime * 1000 * 60;
        // 当前时间戳,单位毫秒
        long nowMilliseconds = getNowMilliseconds();
        // 过期时间 = 当前时间+过期时长
        String overMillTimeStr = String.valueOf(nowMilliseconds + pageOverTimeLong);
        // 控制页面自动退出登录的 redis key
        // hh_auth_page_over_time_with_token:accessToken----overMillTimeStr----pageOverTimeLong
        String pageOverTimeWithTokenKey = RedisKeyUtil.getPageOverTimeWithTokenKey(accessToken);
        redisTemplate.opsForValue().set(pageOverTimeWithTokenKey, overMillTimeStr, pageOverTimeLong, TimeUnit.MILLISECONDS);

        // 全局的过期时长配置,用于延长页面自动退出登录的时间(永不过期,一旦过期就无法使用了)
        // hh_auth_global_page_over_time----String.valueOf(pageOverTimeLong)
        String globalPageOverTimeKey = RedisKeyUtil.getGlobalPageOverTimeKey();
        redisTemplate.opsForValue().set(globalPageOverTimeKey, String.valueOf(pageOverTimeLong));

        // 用户信息(包括角色和权限)存储到redis 过期时间为页面过期时长
        // hh_auth_access_token:accessToken----authenticationInfo----pageOverTimeLong
        String authAccessTokenKey = RedisKeyUtil.getAuthAccessTokenKey(accessToken);
        redisTemplate.opsForValue().set(authAccessTokenKey, JSON.toJSONString(authenticationInfo), pageOverTimeLong, TimeUnit.MILLISECONDS);

        // 用户认证信息(用于刷新token使用) 过期时间JWT过期两倍的时长
        String userId = userInfo.getId();
        Integer expiresIn = authToken.getExpiresIn();
        int refreshExpiresIn = expiresIn * 2;
        // hh_auth_user_authentication:userId----authenticationInfo----refreshExpiresIn
        String refreshUserAuthenticationKey = RedisKeyUtil.getUserAuthenticationKey(userId);
        redisTemplate.opsForValue().set(refreshUserAuthenticationKey, JSON.toJSONString(authenticationInfo), refreshExpiresIn, TimeUnit.SECONDS);
        // hh_auth_refresh_token:refreshToken----authenticationInfo----refreshExpiresIn
        String refreshTokenKey = RedisKeyUtil.getAuthRefreshTokenKey(refreshToken);
        redisTemplate.opsForValue().set(refreshTokenKey, JSON.toJSONString(authenticationInfo), refreshExpiresIn, TimeUnit.SECONDS);

        // 存储用户的登陆token
        // hh_auth_user_login_token:userId----accessToken----expiresIn
        String userLoginTokenKey = RedisKeyUtil.getUserLoginTokenKey(userId);
        redisTemplate.opsForSet().add(userLoginTokenKey, accessToken);
        redisTemplate.expire(userLoginTokenKey, expiresIn, TimeUnit.SECONDS);
        recordLoginState(userInfo.getUsername(), expiresIn);
    }

    /**
     * 记录登陆状态
     */
    private void recordLoginState(String username, Integer pageOverTimeLong) {
        // hh_auth_login_status:username----online----pageOverTimeLong
        String userLoginStatusKey = RedisKeyUtil.getUserLoginStatusKey(username);
        redisTemplate.opsForValue().set(userLoginStatusKey, "online", pageOverTimeLong, TimeUnit.SECONDS);
    }

    /**
     * 当前时间戳,单位为毫秒
     */
    public  long getNowMilliseconds() {
        return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 登陆认证获取访问令牌和用户信息
     *
     * @param loginQo 登录请求体
     * @return AuthenticationInfo
     */
    private AuthenticationInfo checkLogin(LoginQo loginQo) {
        // 密码认证:数据库中的密码是经过bcrypt加密算法存储的
        User user = userService.queryByName(loginQo.getName());
        String dbEncryptPwd = user.getPassword();
        boolean isPassed = BcryptUtil.bEncryptMatch(loginQo.getPassword(), dbEncryptPwd);
        // 登陆失败
        if (!isPassed) {
            log.info("the user: {} login failed, account or password is wrong", user.getId());
            throw new RuntimeException("用户账号或者密码错误");
        }
        // 登录成功,获取用户认证信息
        AuthJwtPrincipal authJwtPrincipal = AuthJwtPrincipal.builder().userId(user.getId()).build();
        AuthenticationInfo authenticationInfo =  obtainAuthenticationInfo(authJwtPrincipal,user);
        String accessToken = authenticationInfo.getAuthToken().getAccessToken();
        if(StringUtils.isBlank(accessToken)){
            throw new RuntimeException("用户账号或者密码错误");
        }
        return authenticationInfo;
    }

    /**
     * 获取认证信息
     * @param authJwtPrincipal 认证主体
     * @param user 用户信息
     * @return AuthenticationInfo
     */
    private AuthenticationInfo obtainAuthenticationInfo(AuthJwtPrincipal authJwtPrincipal,User user) {
        AuthenticationInfo authenticationInfo = new AuthenticationInfo();
        // SpringSecurity Oauth2获取access_token
        AuthToken authToken = getAccessToken(authJwtPrincipal);
        authenticationInfo.setAuthToken(authToken);
        authenticationInfo.setUser(user);
        return authenticationInfo;
    }

    /**
     *  通过登陆信息获取相应的令牌对象
     * @param authJwtPrincipal 认证主体
     * @return AuthToken
     */
    public AuthToken getAccessToken(AuthJwtPrincipal authJwtPrincipal) {
        String loginJsonString = JSON.toJSONString(authJwtPrincipal);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("username", loginJsonString);
        // 因为使用的是自定义认证方式 CustomerAuthProvider,密码用不到,所以设为一个空值即可
        map.add("password", StringUtils.EMPTY);
        map.add("client_id", "client_id");
        map.add("client_secret","client_secret");
        map.add("grant_type", "password");
        map.add("scope", "all");
        // 这里会进行Oauth2.0的请求 在CustomerAuthProvider进行认证处理
        Map response = restTemplate.postForObject("http://127.0.0.1:8081/oauth/token", map, Map.class);
        if (MapUtil.isEmpty(response)) {
            return null;
        }
        // 封装返回
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String) response.get("access_token"));
        authToken.setExpiresIn((Integer) response.get("expires_in"));
        authToken.setRefreshToken((String) response.get("refresh_token"));
        authToken.setTokenType((String) response.get("token_type"));
        authToken.setScope((String) response.get("scope"));
        return authToken;
    }
}

3. 工具类 RedisKeyUtil

/**
 * Redis的Key的生成
 */
public class RedisKeyUtil {

    /**
     * 获取令牌的Key
     *
     * @param token 令牌
     * @return String
     */
    public static String getAuthAccessTokenKey(String token) {
        return String.format(RedisKey.HhAuth.ACCESS_TOKEN, token);
    }

    /**
     * 获取刷新令牌的Key
     *
     * @param refreshToken 刷新令牌
     * @return String
     */
    public static String getAuthRefreshTokenKey(String refreshToken) {
        return String.format(RedisKey.HhAuth.REFRESH_TOKEN, refreshToken);
    }

    /**
     * 全局的页面控制时长
     *
     * @return String
     */
    public static String getGlobalPageOverTimeKey() {
        return RedisKey.HhAuth.GLOBAL_PAGE_OVER_TIME;
    }

    /**
     * token页面控制时长
     *
     * @param token 令牌
     */
    public static String getPageOverTimeWithTokenKey(String token) {
        return String.format(RedisKey.HhAuth.PAGE_OVER_TIME_WITH_TOKEN, token);
    }

    /**
     * 获取用户认证信息
     *
     * @param userId 用户ID
     * @return String
     */
    public static String getUserAuthenticationKey(String userId) {
        return String.format(RedisKey.HhAuth.USER_AUTHENTICATION, userId);
    }

    /**
     * 用户的登陆会话token集合
     *
     * @param userId 用户ID
     * @return String
     */
    public static String getUserLoginTokenKey(String userId) {
        return String.format(RedisKey.HhAuth.USER_LOGIN_TOKEN, userId);
    }

    /**
     * 用户登录状态
     *
     * @param username 用户名
     * @return String
     */
    public static String getUserLoginStatusKey(String username) {
        return String.format(RedisKey.HhAuth.USER_LOGIN_STATUS, username);
    }
}
public interface RedisKey {

    String NGSOC_AUTH_KEY_PREX = "hh_auth_";

    /**
     * 认证相关
     */
    interface HhAuth {
        /**
         * token相关信息
         */
        String ACCESS_TOKEN = StringUtils.join(NGSOC_AUTH_KEY_PREX, "access_token:%s");

        /**
         * refresh_token相关信息
         */
        String REFRESH_TOKEN = StringUtils.join(NGSOC_AUTH_KEY_PREX, "refresh_token:%s");

        /**
         * 全局的页面控制时长
         */
        String GLOBAL_PAGE_OVER_TIME = StringUtils.join(NGSOC_AUTH_KEY_PREX, "global_page_over_time");

        /**
         * token页面控制时长
         */
        String PAGE_OVER_TIME_WITH_TOKEN = StringUtils.join(NGSOC_AUTH_KEY_PREX, "page_over_time_with_token:%s");

        /**
         * 用户认证信息
         */
        String USER_AUTHENTICATION = StringUtils.join(NGSOC_AUTH_KEY_PREX, "user_authentication:%s");

        /**
         * 用户的登陆会话token集合
         */
        String USER_LOGIN_TOKEN = StringUtils.join(NGSOC_AUTH_KEY_PREX, "user_login_token:%s");
        
        /**
         * 用户登录状态
         */
        String USER_LOGIN_STATUS = StringUtils.join(NGSOC_AUTH_KEY_PREX, "login_status:%s");
    }
}

4. 自定义 CustomTokenExtractor 继承 BearerTokenExtractor

在 server-resource 资源服务器服务中添加自定义 CustomTokenExtractor ,获取控制页面自动退出登录的redis key ,如果key还没有到期:

  • 获取全局的过期时长(该key永不过期,存储了延长页面自动退出登录的过期时长),用于延长页面退出登录的过期时间;
  • 延长控制页面退出登录的key的过期时间:当前时间+全局的过期时长;
  • 延长用户token页面过期时间:当前时间+全局的过期时长;
  • 刷新页面的登录状态;
  • 返回accessToken;

如果key已经过期,则直接返回accessToken=null;

@Slf4j
public class CustomTokenExtractor extends BearerTokenExtractor {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 从请求中提取 token
     */
    @Override
    protected String extractToken(HttpServletRequest request) {
        String token = null;
        // 从cookie中获取token,确保tokenCookie.setHttpOnly(true)
        Cookie[] cookies = request.getCookies();
        if (Objects.nonNull(cookies)) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(OAuth2AccessToken.ACCESS_TOKEN)) {
                    token = cookie.getValue();
                    break;
                }
            }
        }

        // 从header中获取token
        if (StringUtils.isEmpty(token)) {
            log.debug("Token not found in cookies. Trying request header.");
            token = extractHeaderToken(request);
        }

        // 从parameters获取token
        if (StringUtils.isEmpty(token)) {
            log.debug("Token not found in headers. Trying request parameters.");
            token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
        }

        if (StringUtils.isEmpty(token)) {
            log.debug("Token not found in headers and request parameters and cookie. Not an OAuth2 request.");
            return null;
        }
        request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
        // 如果redisToken为null,则说明用户未认证
        String redisToken = freshTimeAndGetRedisToken(request, token);
        return redisToken;
    }

    private String freshTimeAndGetRedisToken(HttpServletRequest request, String accessToken) {
        // hh_auth_page_over_time_with_token:accessToken-----overMillTimeStr----pageOverTimeLong
        String pageOverTimeWithTokenKey = RedisKeyUtil.getPageOverTimeWithTokenKey(accessToken);
        String expTimeMilliSecondsStr = redisTemplate.opsForValue().get(pageOverTimeWithTokenKey);
        // 控制页面自动退出登录的key已经到期删除了,直接返回null,token=null用户未认证
        if(StringUtils.isBlank(expTimeMilliSecondsStr)){
            return null;
        }
        // 控制页面自动退出登录的key的过期时间
        long expTimeMilliSeconds = Long.parseLong(expTimeMilliSecondsStr);
        // 当前时间
        long nowMillSecondes = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

        // hh_auth_access_token:accessToken----authenticationInfo----pageOverTimeLong
        String authAccessTokenKey = RedisKeyUtil.getAuthAccessTokenKey(accessToken);
        String authenticationInfoStr = redisTemplate.opsForValue().get(authAccessTokenKey);
        // 说明页面还没退出登录
        if(expTimeMilliSeconds - nowMillSecondes > 0){
            // 获取全局的过期时长,用于延长页面退出登录的过期时间
            // hh_auth_global_page_over_time----String.valueOf(pageOverTimeLong)
            String globalPageOverTimeKey = RedisKeyUtil.getGlobalPageOverTimeKey();
            String pageOverTimeLong = redisTemplate.opsForValue().get(globalPageOverTimeKey);
            long pageTimeoutMilliSeconds = Long.parseLong(pageOverTimeLong);
            // 延长控制页面退出登录的key的过期时间:当前时间+10分钟
            String pageMillTimeOutStr = String.valueOf(nowMillSecondes + pageTimeoutMilliSeconds);
            redisTemplate.opsForValue().set(pageOverTimeWithTokenKey, pageMillTimeOutStr, pageTimeoutMilliSeconds, TimeUnit.MILLISECONDS);
            // 修改用户token页面过期时长
            redisTemplate.expire(authAccessTokenKey, pageTimeoutMilliSeconds, TimeUnit.MILLISECONDS);
            // 刷新当前用户的登陆状态
            freshUserLoginState(authenticationInfoStr,pageTimeoutMilliSeconds);
            return accessToken;
        }else{
            redisTemplate.delete(authAccessTokenKey);
            removeUserLoginState(authenticationInfoStr);
        }
        return null;
    }

    /**
     * 刷新当前用户的登陆状态
     */
    private void freshUserLoginState( String authenticationInfoStr,Long pageTimeout) {
        AuthenticationInfo authenticationInfo = JSONUtil.toBean(authenticationInfoStr, AuthenticationInfo.class);
        String userLoginStatusKey = RedisKeyUtil.getUserLoginStatusKey(authenticationInfo.getUser().getUsername());
        redisTemplate.opsForValue().set(userLoginStatusKey, "online", pageTimeout, TimeUnit.MILLISECONDS);
    }

    /**
     * 清除用户的登陆状态
     */
    private void removeUserLoginState( String authenticationInfoStr) {
        AuthenticationInfo authenticationInfo = JSONUtil.toBean(authenticationInfoStr, AuthenticationInfo.class);
        String userLoginStatusKey = RedisKeyUtil.getUserLoginStatusKey(authenticationInfo.getUser().getUsername());
        redisTemplate.delete(userLoginStatusKey);
    }
}

5. 资源服务器配置类 ResourceServerAutoConfiguration

@Slf4j
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerAutoConfiguration extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Value("${spring.application.name}")
    private String appName;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(appName);
        resources.tokenStore(tokenStore);
        // 配置自定义的TokenExtractor
        resources.tokenExtractor(tokenExtractor());
    }

    @Bean
    @Primary
    public TokenExtractor tokenExtractor() {
        CustomTokenExtractor customTokenExtractor = new CustomTokenExtractor();
        return customTokenExtractor;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 放行的请求
                .antMatchers("/api/v1/login").permitAll()
                // 其他请求必须认证才能访问
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }
}

2. 延长系统退出登录的时间-源码流程分析

1. 用户登录获取令牌

在这里插入图片描述

登录成功后redis中存储的key,其中TOKEN开头的key为SpringSecurity Oauth2相关的key:

在这里插入图片描述

可以看到控制系统页面自动退出登录的时间的key的过期时长一致在减少,当过期时长为0时,key被删除,根据前面文章的源码分析我们知道请求会先经过OAuth2AuthenticationProcessingFilter过滤器,该过滤器第一步就是提取token,如果token=null代表用户未认证,因此我们可以利用这一逻辑,实现一个自定义的 CustomTokenExtractor ,在请求进来的时候判断控制页面自动退出登录的 redis key是否存在,如果不存在说明已经到达系统自动退出登录的时间,返回给OAuth2AuthenticationProcessingFilter过滤器token=null,用户未认证信息。如果key存在就延长key的过期时间。下面我们来看源码分析下整个过程。

2. 通过访问令牌访问系统资源 HelloController

@RestController
@RequestMapping("/api/v1")
public class HelloController {

    @GetMapping("/hello")
    public String hello(HttpServletRequest request){
        String username = UserInfoShareHolder.getUserInfo().getUsername();
        return username;
    }
}

在这里插入图片描述

3. 源码分析

1. 请求进入过滤器 OAuth2AuthenticationProcessingFilter#doFilter 方法

请求首先被OAuth2AuthenticationProcessingFilter过滤器拦截,在该过滤器的doFilter方法中主要做了以下事情:

  • 从请求中提取 token 并获取待认证的Authentication 对象: Authentication authentication = tokenExtractor.extract(request);
  • request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
  • 通过待认证的Authentication对象倒TokenStore中获取完成的Authentication对象:Authentication authResult = authenticationManager.authenticate(authentication);
  • 发布认证成功的事件通知:eventPublisher.publishAuthenticationSuccess(authResult);
  • SecurityContextHolder.getContext().setAuthentication(authResult);
  • 进入过滤器链中的下一个过滤器;

当用户登录后,如果有请求访问系统将会延长系统退出登录的时间:我们要做的就是实现自定义CustomTokenExtractor继承自BearerTokenExtractor,然后在提取token时判断控制页面自动退出登录的key是否存在,如果key存在就延长key的过期时长。

在这里插入图片描述

2. 请求进入 BearerTokenExtractor#extract 方法

在这里插入图片描述

3. 请求进入自定义 CustomTokenExtractor#extractToken 方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4. 请求进入拦截器 UserInfoInterceptor#preHandler 方法

在这里插入图片描述

5. 请求进入控制器 HelloController#hello方法

在这里插入图片描述

至此,整个流程就分析完了,核心点就在CustomTokenExtractor类的实现上。

3. 页面自动退出登录-源码流程分析

我们要做的就是实现自定义CustomTokenExtractor继承自BearerTokenExtractor,然后在提取token时判断控制页面自动退出登录的key是否存在,如果key不存在说明已经达到页面自动退出登录的时长,那么accessToken=null,OAuth2AuthenticationProcessingFilter#doFilter 方法在判断accessToken=null后就会进入下一个过滤器,最终响应用户未认证的信息,页面自动退出登录。

redis中控制页面自动登录的key已经过期,(hh_auth_page_over_time_with_token:accessToken)此时如果访问系统资源将响应用户未认证的异常消息。

在这里插入图片描述

1. 通过访问令牌访问系统资源 HelloController

@RestController
@RequestMapping("/api/v1")
public class HelloController {

    @GetMapping("/hello")
    public String hello(HttpServletRequest request){
        String username = UserInfoShareHolder.getUserInfo().getUsername();
        return username;
    }
}

判断redis中控制页面自动登录的key是否过期,如果过期删除则返回accessToken=null,代表用户未认证,需要重新登录系统。

在这里插入图片描述

2. 源码分析

1. 请求进入过滤器 OAuth2AuthenticationProcessingFilter#doFilter 方法

在这里插入图片描述

2. 请求进入 BearerTokenExtractor#extract 方法

在这里插入图片描述

3. 请求进入自定义 CustomTokenExtractor#extractToken 方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
spring-security-oauth2-2.2.3.release.jar是一个用于实现基于OAuth 2.0协议的安全认证和授权的Java库。OAuth 2.0协议是一种用于授权的开放标准,它允许用户通过授权代理向第三方应用程序授权访问受保护的资源,而无需将用户的凭证(例如用户名和密码)透露给第三方。 spring-security-oauth2-2.2.3.release.jar提供了一套面向Spring应用程序的OAuth 2.0认证和授权解决方案。它可以轻松地将OAuth 2.0集成到现有的Spring应用程序中,并提供了一系列的API和类,使开发人员可以方便地实现OAuth 2.0认证和授权流程。 使用spring-security-oauth2-2.2.3.release.jar,开发人员可以通过配置和自定义一些核心组件(例如TokenStore、AuthorizationServer和ResourceServer)来实现OAuth 2.0的四种授权类型:授权码模式、密码模式、客户端模式和简化模式。通过这些授权类型,开发人员可以实现不同的应用场景和需求。 此外,spring-security-oauth2-2.2.3.release.jar支持使用不同的存储机制来存储和管理OAuth 2.0令牌,例如内存存储、数据库存储和Redis存储。开发人员可以根据自己的需求选择并配置合适的存储机制。 总的来说,spring-security-oauth2-2.2.3.release.jar提供了一种简单、可扩展且安全的方式来实现OAuth 2.0认证和授权。无论您是开发面向Web、移动还是其他类型应用程序,该库都能帮助您轻松地实现与第三方应用程序的安全集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值