欢迎来到Introzo百科
Introzo百科
当前位置:网站首页 > 体育 > 如何实现 Oauth2 自定义登录接口以及返回自定义格式?

如何实现 Oauth2 自定义登录接口以及返回自定义格式?

日期:2023-09-05 14:54

今天更新文章的原因是粉丝提了个问题:如何实现Oauth2认证服务器自定义登录接口以及返回自定义格式? 这里我给大家分享一个简单且实用的方法,既可以灵活定制登录参数也可以自行组装返回结果。

实现方案

我们知道,认证服务器生成token的入口是TokenEndpoint#postAccessToken(Principal principal, @RequestParam Map parameters),那我们就可以直接在认证服务器自定义一个登录接口,然后组装好TokenEndpoint#postAccessToken()需要的参数,直接调用它生成token后再封装成我们需要的格式即可。

接下来我们直接进入实战:

1. 定义登录参数

/** * 自定义登录参数 * @author JAVA日知录 * @date 2022/5/14 09:23 */@Datapublic class LoginRequest { private String userName; private String password; private String grantType; private String mobile; private String smsCode;}

为了兼容密码模式和自定义的短信验证码模式,我们将所有的参数都放入一个实体,大家可以根据自己的项目需要自行封装。

2. 创建一个登录类型的枚举

public enum LoginTypeEnum { /** * 密码模式 */ PASSWORD(\"password\"), /** * 短信验证码模式 */ SMSCODE(\"sms_code\"); private final String grantType; LoginTypeEnum(String grantType) { this.grantType = grantType; } public String getGrantType() { return grantType; } public static LoginTypeEnum fromGrantType(String grantType){ return www.introzo.com(LoginTypeEnum.values()) .filter(item -> item.getGrantType().equals(grantType)) .findFirst() .orElseThrow(()-> new BizException(\"不支持此登录类型\")); }}

3. 创建自定义登录接口(关键)

@RestController@RequestMapping(\"/token\")@Slf4j@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class AuthController { private final TokenStore tokenStore; private final TokenEndpoint tokenEndpoint; private final RedisTemplate redisTemplate; /** * 自定义登录接口 * @return */ @PostMapping(\"login\") public ResultData login(@RequestBody LoginRequest loginRequest) throws HttpRequestMethodNotSupportedException { Assert.isTrue(StringUtils.isNotEmpty(loginRequest.getGrantType()), \"请在参数中指定登录类型grantType\"); LoginTypeEnum typeEnum = LoginTypeEnum.fromGrantType(loginRequest.getGrantType()); //注入clientId 和 password // 可以通过Header传入client 和 secret User clientUser = new User(\"jianzh5\", \"jianzh5\", new ArrayList()); Authentication token = new UsernamePasswordAuthenticationToken(clientUser, null, new ArrayList()); //构建密码登录 Map map = new HashMap(); switch (typeEnum){ case PASSWORD : { map.put(\"username\", loginRequest.getUserName()); map.put(\"password\", loginRequest.getPassword()); map.put(\"grant_type\", \"password\"); break; } case SMSCODE:{ map.put(\"smsCode\", loginRequest.getSmsCode()); map.put(\"mobile\", loginRequest.getMobile()); map.put(\"grant_type\", \"sms_code\"); break; } default: throw new BizException(\"不支持的登录类型\"); } OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(token,map).getBody(); return ResultData.success(oAuth2AccessToken); } ...}

这里我们将TokenEndpoint注入,然后伪装一个客户端的认证流程,调用TokenEndpoint.postAccessToken()获取接口。

这里我们写死了client信息,实际上也可以通过Header请求头传入或者通过配置文件注入。

4. 在安全配置类中放行登录接口

@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... @Override protected void configure(HttpSecurity http) throws Exception { // 加入验证码登陆 http.apply(smsCodeSecurityConfig); http .authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() .and() .authorizeRequests().antMatchers(\"/token/**\",\"/sms/**\").permitAll() .anyRequest().authenticated() .and() .csrf() .disable(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers( \"/error\", \"/static/**\", \"/v2/api-docs/**\", \"/swagger-resources/**\", \"/webjars/**\", \"/favicon.ico\" ); }}

这个安全配置类中有两个放行策略,一个通过permitAll()实现,一个通过web.ignoring()实现,他们两个的区别是:

web ignoring()比较适合配置前端相关的静态资源,它是完全绕过spring security得所有filter的;permitAll(),会给没有登录的用户适配一个AnonymousAuthenticationToken,设置到SecurityContextHolder,方便后面的filter可以统一处理authentication。

{\"userName\":\"zhangjian\",\"password\":\"111111\",\"grantType\":\"password\",\"mobile\":\"18888887777\",\"smsCode\":\"666666\"}

测试

1. 自定义密码登录

2. 自定义短信验证码登录

3. 不支持的登录类型

小结

本文提供的方案是将登录接口与认证服务器放在一起,如果在项目中由于某些原因不方便将其放在认证服务中,也可以让认证服务器提供一个Feign接口,然后让后端服务调用此接口进行登录即可。

来源:公众号——JAVA日知录

关灯