总结项目中oauth2模块的配置流程及实际业务oauth2认证记录(Spring Security)

发布时间:2023年12月31日


项目中用过的spring security,拿来温习一下,一个简单版本的笔记。有问题欢迎大佬们指正!

简单示例

添加oauth2的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

spring-boot-starter-security是Spring Security基础依赖,spring-security-oauth2-autoconfigure是OAuth2自动配置模块

配置认证服务器

配置OAuth2认证服务器。在Spring Boot中,可以通过创建一个@Configuration类并使用@EnableAuthorizationServer注解来实现。

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client")
            .secret("{noop}secret") // 设置客户端的密码
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(3600)// 设置访问令牌有效期为1800秒
            .refreshTokenValiditySeconds(86400);// 设置刷新令牌有效期为86400秒
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

上面代码中:创建了一个名为OAuth2Config的配置类并启用了@EnableAuthorizationServer注解,这表示我们正在创建一个OAuth2认证服务器。

通过configure(ClientDetailsServiceConfigurer clients)方法来配置OAuth2客户端详情服务,这里使用了内存存储客户端信息。configure(AuthorizationServerEndpointsConfigurer endpoints)方法用于配置OAuth2认证服务器的终端点,这里将认证管理器设置为authenticationManager

配置资源服务器

配置资源服务器,以便我们的应用程序能够保护资源并要求访问令牌进行身份验证和授权。

可以通过创建一个@Configuration类并标注@EnableResourceServer注解来实现:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/api/**").authenticated()
            .and().csrf().disable();
    }
}

创建了一个名为ResourceServerConfig的配置类并启用了@EnableResourceServer注解,这表示我们正在创建一个OAuth2资源服务器。

此外,我们通过configure(HttpSecurity http)方法来配置HTTP安全性,这里我们要求任何访问/api/**的请求都必须经过身份验证才能访问。

配置安全

需要配置安全,以便我们的应用程序可以使用安全协议来保护和管理OAuth2令牌。

可以通过创建一个@Configuration类并标注@EnableWebSecurity注解来实现:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/oauth/token").permitAll()
            .anyRequest().authenticated()
            .and().csrf().disable();
    }
}

创建了一个名为SecurityConfig的配置类并启用了@EnableWebSecurity注解,这表示我们正在创建一个Spring Security安全配置。

此外,我们通过configure(AuthenticationManagerBuilder auth)方法来配置用户详细信息服务和密码编码器。configure(HttpSecurity http)方法用于配置HTTP安全性,这里我们允许任何人访问/oauth/token端点,但对所有其他请求都要求进行身份验证。

使用http或者curl命令测试

# 获取访问令牌
curl -X POST \
  http://localhost:8080/oauth/token \
  -H 'Authorization: Basic Y2xpZW50OnNlY3JldA==' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=password&username=user&password=password'

# 使用访问令牌访问受保护的资源
curl -X GET \
  http://localhost:8080/api/protected \
  -H 'Authorization: Bearer <access_token>'

上面的命令中的Authorization头部的内容是base64编码的client:secret。在实际使用中,你需要替换成你自己的客户端ID和秘钥。

这是一个简单的使用Spring Boot和OAuth2实现认证和授权的示例。

实际业务中工具类(记录):

认证服务器

package com.youming.shuiku.oauth2.config;

import com.youming.shuiku.oauth2.authentication.ding.DingTokenGranter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * Description:
 * </p>
 *
 * @author wangxihao
 * @version v1.0.0
 * @ClassName AuthorizationServerConfiguration
 * @Date 2022/10/20 15:15
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private BCryptPasswordEncoder passwordEncoder;

	/**
	 * 注入用于支持 password 模式
	 */
	@Autowired
	private AuthenticationManager authenticationManager;

	// // 第一处修改,注入 RedisConnectionFactory,用于连接 Redis
	@Autowired
	private RedisConnectionFactory redisConnectionFactory;

	@Lazy
	@Resource(name = "userDetailsService")
	private UserDetailsService userDetailsService;

	@Bean
	@Primary
	@ConfigurationProperties(prefix = "spring.datasource")
	public DataSource dataSource() {
		// 配置数据源(注意,我使用的是 HikariCP 连接池),以上注解是指定数据源,否则会有冲突
		return DataSourceBuilder.create().build();
	}

	@Bean
	public TokenStore tokenStore() {
		// 基于 JDBC 实现,令牌保存到数据库
		// return new JdbcTokenStore(dataSource());
		return new RedisTokenStore(redisConnectionFactory);
	}

	// @Bean
	// @Primary
	// public TokenStore jdbcTokenStore(){
	// return new JdbcTokenStore(dataSource());
	// }

	@Bean
	public ClientDetailsService jdbcClientDetailsService() {
		// 基于 JDBC 实现,需要事先在数据库配置客户端信息
		return new JdbcClientDetailsService(dataSource());
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		// 用于支持密码模式
		// endpoints.authenticationManager(authenticationManager)
		// // 增加 TokenStore 配置
		// .tokenStore(tokenStore());
		// endpoints.userDetailsService(userDetailsService);

		// 获取原有默认授权模式(授权码模式、密码模式、客户端模式、简化模式)的授权者
		List<TokenGranter> granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));

		// 添加钉钉小程序授权模式的授权者
		granterList.add(new DingTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
				endpoints.getOAuth2RequestFactory(), authenticationManager));

		CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
		endpoints.authenticationManager(authenticationManager).tokenGranter(compositeTokenGranter)
				.userDetailsService(userDetailsService).tokenStore(tokenStore());
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.allowFormAuthenticationForClients()
				// 允许客户端访问 /oauth/check_token 检查 token
				// .checkTokenAccess("isAuthenticated()");
				// .tokenKeyAccess("permitAll()")z
				.checkTokenAccess("permitAll()");
	}

	/**
	 * 配置客户端
	 * @param clients
	 * @throws Exception
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 客户端配置
		clients.withClientDetails(jdbcClientDetailsService());
		/*
		 * clients // 使用内存设置 .inMemory() // client_id .withClient("client") //
		 * client_secret .secret(passwordEncoder.encode("secret")) // 授权类型,密码模式和刷新令牌
		 * .authorizedGrantTypes("password", "refresh_token") // 授权范围 .scopes("backend")
		 * // 可以设置对哪些资源有访问权限,不设置则全部资源都可以访问 .resourceIds("backend-resources") //
		 * 设置访问令牌的有效期,这里是 1 天 .accessTokenValiditySeconds(60 * 60 * 24) // 设置刷新令牌的有效期,这里是
		 * 30 天 .refreshTokenValiditySeconds(60 * 60 * 24 * 30);
		 */
	}

}

资源服务器、配置安全

package com.youming.shuiku.oauth2.config;

import com.youming.shuiku.oauth2.authentication.ding.DingAuthenticationProvider;
import com.youming.shuiku.oauth2.config.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

/**
 * 认证服务器安全配置
 * <p>
 * Description:
 * </p>
 *
 * @author wangxihao
 * @version v1.0.0
 * @ClassName WebSecurityConfiguration
 * @Date 2022/10/20 15:11
 */
@Configuration
@EnableWebSecurity
// 增加了资源服务器配置
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		// 配置默认的加密方式
		return new BCryptPasswordEncoder();
	}

	@Bean
	@Override
	protected UserDetailsServiceImpl userDetailsService() {
		return new UserDetailsServiceImpl();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// 使用自定义认证与授权
		// auth.userDetailsService(userDetailsService());
		auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(dingAuthenticationProvider());
	}

	/**
	 * 用于支持 password 模式
	 * @return
	 * @throws Exception
	 */
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/user/login")
				.antMatchers("/user/refresh")
				.antMatchers("/user/logins")
				// .antMatchers("/info")
				.antMatchers("/logout").antMatchers("/wx/**");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
				.authorizeRequests().antMatchers("/login/**").permitAll()
		// .and()
		// .authorizeRequests()
		// 增加了授权访问配置
		// .antMatchers("/user/info").hasAuthority("USER")
		// .antMatchers("/user/logout").hasAuthority("USER")
		;
	}

	/**
	 * 钉钉小程序认证授权提供者
	 * @return
	 */
	@Bean
	public DingAuthenticationProvider dingAuthenticationProvider() {
		DingAuthenticationProvider provider = new DingAuthenticationProvider();
		provider.setUserDetailsService(userDetailsService());
		// provider.setWxMaService(wxMaService);
		// provider.setMemberFeignClient(memberFeignClient);
		return provider;
	}

	/**
	 * 用户名密码认证授权提供者
	 * @return
	 */
	@Bean
	public DaoAuthenticationProvider daoAuthenticationProvider() {
		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
		provider.setUserDetailsService(userDetailsService());
		provider.setPasswordEncoder(passwordEncoder());
		provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常,默认:true-隐藏;false-抛出异常;
		return provider;
	}

}

用户验证

对于用户验证,你可以实现UserDetailsService接口来定义从数据库或其他数据源中获取用户信息的逻辑

package com.youming.shuiku.oauth2.config.service;

import com.youming.shuiku.commons.constant.BaseConstant;
import com.youming.shuiku.commons.domain.SysUser;
import com.youming.shuiku.commons.response.ResponseCode;
import com.youming.shuiku.oauth2.service.ISysUserService;
import com.youming.shuiku.oauth2.userdetails.SysUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
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.Service;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private ISysUserService sysUserService;

	// @Autowired
	// private IWechatUserRelService wechatUserRelService;

	// @Autowired
	// private ITbPermissionService tbPermissionService;

	@Override
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		// SysUser sysUser = sysUserService.getByUsername(s);
		// List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
		// grantedAuthorities.add(new SimpleGrantedAuthority("USER"));
		//
		// // 用户存在
		// if (sysUser != null) {
		// return new User(sysUser.getLoginAcct(), sysUser.getPassword(),
		// grantedAuthorities);
		// }
		//
		// // 用户不存在
		// else {
		// return null;
		// }
		SysUserDetails userDetails = null;

		SysUser sysUser = sysUserService.getByMobile(s);
		if (sysUser != null) {
			if (sysUser.getUserType() == BaseConstant.UserType.THREE) {
				userDetails = new SysUserDetails(sysUser, "THREE");
			}
			else {
				userDetails = new SysUserDetails(sysUser, "USER");
			}
		}
		if (userDetails == null) {
			throw new UsernameNotFoundException(ResponseCode.USER_NOT_EXIST.message());
		}
		else if (!userDetails.isEnabled()) {
			throw new DisabledException("该账户已被禁用!");
		}
		else if (!userDetails.isAccountNonLocked()) {
			throw new LockedException("该账号已被锁定!");
		}
		else if (!userDetails.isAccountNonExpired()) {
			throw new AccountExpiredException("该账号已过期!");
		}
		return userDetails;

	}

	/**
	 * userId 认证方式
	 * @param userId
	 * @return
	 */
	public UserDetails loadUserByUserId(String userId) {
		SysUserDetails userDetails = null;
		SysUser sysUser = sysUserService.getByUserId(userId);
		if (sysUser != null) {
			userDetails = new SysUserDetails(sysUser, "USER");
		}

		if (userDetails == null) {
			throw new UsernameNotFoundException(ResponseCode.USER_NOT_EXIST.message());
		}
		else if (!userDetails.isEnabled()) {
			// throw new DisabledException("该账户已被禁用!");
			throw new DisabledException("用户已离职!");
		}
		else if (!userDetails.isAccountNonLocked()) {
			throw new LockedException("该账号已被锁定!");
		}
		else if (!userDetails.isAccountNonExpired()) {
			throw new AccountExpiredException("该账号已过期!");
		}
		return userDetails;
	}

}

登录控制层

package com.youming.shuiku.oauth2.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.youming.shuiku.commons.base.web.Header;
import com.youming.shuiku.commons.domain.LoginInfo;
import com.youming.shuiku.commons.domain.SysUser;
import com.youming.shuiku.commons.exception.BusinessException;
import com.youming.shuiku.commons.response.ResponseCode;
import com.youming.shuiku.commons.response.ResponseResult;
import com.youming.shuiku.commons.util.RedisUtil;
import com.youming.shuiku.oauth2.Vo.LoginParam;
import com.youming.shuiku.oauth2.Vo.RefreshParam;
import com.youming.shuiku.oauth2.constant.OAuth2Constant;
import com.youming.shuiku.oauth2.dto.TokenDto;
import com.youming.shuiku.oauth2.service.ISysUserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author wangxihao
 * @since 2022-11-10
 */
@RestController
@RequestMapping("/user")
public class LoginController {

    @Value("${security.oauth2.client.access-token-uri}")
    public String accessTokenUri;

    @Value("${security.oauth2.resource.token-info-uri}")
    public String tokenInfoUri;

    @Value("${security.oauth2.client.client-id}")
    public String clientId;

    @Value("${security.oauth2.client.client-secret}")
    public String clientSecret;

    @Value("${security.oauth2.dingding.client-id}")
    public String dingdingClientId;

    @Value("${security.oauth2.dingding.client-secret}")
    public String dingdingClientSecret;

    @Resource
    private HttpServletRequest request;

    @Resource
    ISysUserService sysUserService;

    // @Resource
    // private IUserDeptService userDeptServicel;

    @Resource
    RedisUtil redisUtil;

    @Resource
    public BCryptPasswordEncoder passwordEncoder;

    /**
     * 管理员登录
     *
     * @param loginParam {@code JSON} {@link LoginParam}
     * @return {@link ResponseResult}
     */
    @PostMapping("login")
    public ResponseResult admin(@RequestBody LoginParam loginParam) {
        Integer platformType = loginParam.getPlatformType();
        if (platformType == OAuth2Constant.LoginType.DINGDIND_APPLET) {
            return dingdingLogin(loginParam);
        } else if (platformType == OAuth2Constant.LoginType.WEB) {
            return pcLogin(loginParam);
        } else if (platformType == OAuth2Constant.LoginType.THREE) {
            return threeLogin(loginParam);
        } else {
            return ResponseResult.failure();
        }
    }

    /**
     * 三方登录
     * 提供三方系统以客户端模式登录
     *
     * @param loginParam {@code JSON} {@link LoginParam}
     * @return {@link ResponseResult}
     */
    public ResponseResult threeLogin(LoginParam loginParam) {
        if (StringUtils.isEmpty(loginParam.getAppId()) || StringUtils.isEmpty(loginParam.getSecret())) {
            throw new BusinessException(ResponseCode.PARAM_NOT_COMPLETE);
        }
        Map<String, Object> authParam = new HashMap<>();
        // 三方登录客户端和密码接口回传
        authParam.put("client_id", loginParam.getAppId());
        authParam.put("client_secret", loginParam.getSecret());
        authParam.put("grant_type", "client_credentials");
        String strJson = HttpUtil.post(accessTokenUri, authParam);
        JSONObject jsonObject = JSONUtil.parseObj(strJson);
        String token = String.valueOf(jsonObject.get("access_token"));

        if ("null".equals(token)) {
            return ResponseResult.failure(String.valueOf(jsonObject.get("error_description")));
        }
        return ResponseResult.success(TokenDto.of(token, null));

    }

    /**
     * 钉钉小程序登录
     *
     * @param loginParam
     * @return
     * @throws
     * @author terry
     * @date 2022/4/13 20:31
     */
    private ResponseResult dingdingLogin(LoginParam loginParam) {
        SysUser sysUser = sysUserService.getByUserId(loginParam.getUserId());
        // 通过 HTTP 客户端请求登录接口
        Map<String, Object> authParam = getAuthParam(2);
        authParam.put("userId", sysUser.getUserId());
        authParam.put("grant_type", "dingding");
        return loginAccessToken(sysUser, authParam);
    }

    /**
     * pc端登录--
     *
     * @param loginParam
     * @return
     * @throws
     * @author terry
     * @date 2022/4/13 20:30
     */
    private ResponseResult pcLogin(LoginParam loginParam) {
        SysUser sysUser = sysUserService.getByMobile(loginParam.getUsername());
        if (sysUser == null) {
            throw new BusinessException(ResponseCode.USER_LOGIN_ERROR);
        }
        if (sysUser.getState() != 1) {
            throw new BusinessException(ResponseCode.User_DISABLED);
        }
        if(sysUser.getIsDel() == 1){
            throw new BusinessException(ResponseCode.USER_DELETE);
        }
        // 验证密码是否正确
        if (!passwordEncoder.matches(loginParam.getPassword(), sysUser.getPassword())) {
            throw new BusinessException(ResponseCode.USER_LOGIN_ERROR);
        }

        // 通过 HTTP 客户端请求登录接口
        Map<String, Object> authParam = getAuthParam(1);

        // 三方登录客户端和密码接口回传
        if (loginParam.getPlatformType() == OAuth2Constant.LoginType.THREE) {
            authParam.put("client_id", loginParam.getAppId());
            authParam.put("client_secret", loginParam.getSecret());
        }
        authParam.put("username", loginParam.getUsername());
        authParam.put("password", loginParam.getPassword());
        authParam.put("grant_type", "password");
        // 获取accessToken
        return loginAccessToken(sysUser, authParam);
    }

    /**
     * 登录系统获取token
     *
     * @param sysUser
     * @param authParam
     * @return
     * @throws
     * @author terry
     * @date 2022/4/13 21:25
     */
    private ResponseResult loginAccessToken(SysUser sysUser, Map<String, Object> authParam) {
        String strJson = HttpUtil.post(accessTokenUri, authParam);
        JSONObject jsonObject = JSONUtil.parseObj(strJson);
        String token = String.valueOf(jsonObject.get("access_token"));
        String refresh = String.valueOf(jsonObject.get("refresh_token"));

        if ("null".equals(token)) {
            return ResponseResult.failure(String.valueOf(jsonObject.get("error_description")));
        }
        sysUser.setPassword("");
//        redisUtil.setex("user:" + token, sysUser, 86400);
        LoginInfo loginInfo = new LoginInfo();
        BeanUtil.copyProperties(sysUser, loginInfo);
        redisUtil.setex("loginInfo:" + token, loginInfo, 86400);
        return ResponseResult.success(TokenDto.of(token, refresh));
    }

    /**
     * 刷新令牌
     *
     * @return {@link ResponseResult}
     */
    @PostMapping("refresh")
    public ResponseResult refresh(@RequestBody RefreshParam refreshParam) {
        String accessToken = Header.getAuthorization(request.getHeader("Authorization"));

        Map<String, Object> authParam = getAuthParam(1);
        authParam.put("grant_type", "refresh_token");
        authParam.put("refresh_token",refreshParam.getRefresh());
        String strJson = HttpUtil.post(accessTokenUri, authParam);

        JSONObject jsonObject = JSONUtil.parseObj(strJson);
        String token = String.valueOf(jsonObject.get("access_token"));
        String refresh = String.valueOf(jsonObject.get("refresh_token"));

        if ("null".equals(token)) {
            return ResponseResult.failure(String.valueOf(jsonObject.get("error_description")));
        }
//        SysUser sysUser = (SysUser) redisUtil.get("user:" + accessToken);
        redisUtil.del("user:" + accessToken);
//        redisUtil.setex("user:" + token, sysUser, 86400);

        LoginInfo loginInfo =  (LoginInfo) redisUtil.get("loginInfo:" + accessToken);
        redisUtil.del("loginInfo:" + accessToken);
        redisUtil.setex("loginInfo:" + token, loginInfo, 86400);
        return ResponseResult.success(TokenDto.of(token, refresh));

        /*
         * // AccessToken不存在直接返回null String refreshToken =
         * refreshTokenMap.get(accessToken); if (StrUtil.isBlank(refreshToken)) { throw
         * new BusinessException(ResponseCode.USER_NOT_LOGGED_IN); } // 通过HTTP 客户端请求刷新接口
         * // 通过http 客户端请求登录接口 Map<String, Object> authParam = getAuthParam();
         * authParam.put("grant_type", "refresh_token"); authParam.put("refresh_token",
         * refreshToken);
         *
         * // 获取accessToken String strJson = HttpUtil.post(accessTokenUri, authParam);
         * JSONObject jsonObject = JSONUtil.parseObj(strJson); String token =
         * String.valueOf(jsonObject.get("access_token")); String refresh =
         * String.valueOf(jsonObject.get("refresh_token")); if (StrUtil.isNotBlank(token)
         * && StrUtil.isNotBlank(refresh)) { // 删除旧Token
         * refreshTokenMap.remove(accessToken); // 将refresh_Token保存到服务端
         * refreshTokenMap.put(token, refresh); result.put("token", token);
         * ResponseResult.success(token); }
         */
    }

    /**
     * 刷新令牌
     *
     * @return {@link ResponseResult}
     */
    /*
     * @GetMapping("info")
     *
     * @MyLog(value = "获取用户信息") // 这里添加了AOP的自定义注解 public ResponseResult info() { //
     * 获取accessToken String userName =
     * SecurityContextHolder.getContext().getAuthentication().getName();
     *
     * SysUser sysUser = sysUserService.getByUsername(userName); UserInfoDto dto = new
     * UserInfoDto(); BeanUtils.copyProperties(sysUser, dto); return
     * ResponseResult.success(dto); }
     */

    // 私有方法-----------------------------------
    private Map<String, Object> getAuthParam(int type) {
        Map<String, Object> authParam = new HashMap<>();
        if (type == 1) {
            authParam.put("client_id", clientId);
            authParam.put("client_secret", clientSecret);
        } else if (type == 2) {
            authParam.put("client_id", dingdingClientId);
            authParam.put("client_secret", dingdingClientSecret);
        }
        return authParam;
    }

    @Resource
    public TokenStore tokenStore;

    @PostMapping("logout")
    private ResponseResult logout(@RequestParam(value = "exit", defaultValue = "false") Boolean isExit) {
        String token = Header.getAuthorization(request.getHeader("Authorization"));
        // 删除token以注销
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
        if (null != oAuth2AccessToken) {
            if (isExit) {
                tokenStore.removeAccessToken(oAuth2AccessToken);
            }
            return ResponseResult.success();
        }
        return ResponseResult.failure(ResponseCode.INTERFACE_ADDRESS_INVALID);
    }

}

配置文件application.yml

base:
  config:
    oauth:
      hostname: 192.168.xx.xx
      port: xxxx
    nacos:
      hostname: 192.168.xx.xx
      port: xxxx
    tidb:
      hostname: 192.168.xx.xx
      port: xxxx
    redis:
      hostname: 192.168.xx.xx
      port: xxxx
      password: xxxxxxxxxxx
#    mongodb:
#      hostname: 192.168.xx.xx
      port: xxxx




spring:
  application:
    name: oauth2
  main:
    allow-bean-definition-overriding: true
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  cloud:
    nacos:
      discovery:
        server-addr: ${base.config.nacos.hostname}:${base.config.nacos.port}
        group: SHUIKU_GROUP
#        namespace: 28ac7d69-8afd-474d-924d-28a291330188
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://${base.config.tidb.hostname}:${base.config.tidb.port}/shuiku_base?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: xxxxxxxxx
    hikari:
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      pool-name: MyHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1
  redis:
    # 你 Redis 主机地址
    host: ${base.config.redis.hostname}
    # 你 Redis 主机端口
    port: ${base.config.redis.port}
    # Redis服务器连接密码(默认为空)
    password: ${base.config.redis.password}
    # 我们使用 Lettuce 客户端,比 Jedis 更高效
    lettuce:
      # 连接池配置
      pool:
        # 连接池中的最小空闲连接,默认 0
        min-idle: 0
        # 连接池中的最大空闲连接,默认 8
        max-idle: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制),默认 -1ms
        max-wait: -1ms
        # 连接池最大连接数(使用负值表示没有限制),默认 8
        max-active: 8


server:
  port: 9002


#logback
logging:
  level:
    com.youming.youche.oauth2: info
  #将日志输出到文件
  config: classpath:oauth2-log.xml



security:
  oauth2:
    client:
      client-id: oauth
      client-secret: oauth
      access-token-uri: http://localhost:${server.port}/oauth/token
      user-authorization-uri: http://localhost:${server.port}/oauth/authorize
    resource:
      token-info-uri: http://localhost:${server.port}/oauth/check_token
    authorization:
      check-token-access: http://localhost:${server.port}/oauth/check_token
    dingding:
      client-id: dingding
      client-secret: dingding

文章来源:https://blog.csdn.net/wang121213145/article/details/135320365
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。