在Spring的强大生态中,我们很多时候只需要简单的配置就可以完成想要的需求。在Grtblog中,我使用了其提供的能力快速完成了Oauth2Client配置,实现了第三方账号的快速注册以及登录,更便于用户快速获得账号参与评论,提升用户体验。
安装依赖
在pom.xml中添加相关的依赖并同步即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
创建流程
相关配置
得益于依赖的强大封装,我们只需要在application.yml中配置好Oauth应用的信息即可
我们这里以Github和Google为例:
spring:
security:
oauth2:
client:
registration:
google:
client-id: your_client_id
client-secret: your_client_secret
scope: profile, email
redirect-uri: "https://your_deploy_site/api/v1/login/oauth2/code/google" //回调地址,其默认端点为/login/oauth2/code/google
client-name: Google
github:
client-id: your_client_id
client-secret: your_client_secret
redirect-uri: "https://your_deploy_site/api/v1/login/oauth2/code/github"
scope: user:email
client-name: Github
provider:
google:
authorization-uri: https://accounts.google.com/o/oauth2/auth
token-uri: https://oauth2.googleapis.com/token
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
user-name-attribute: sub
github:
authorization-uri: https://github.com/login/oauth/authorize
token-uri: https://github.com/login/oauth/access_token
user-info-uri: https://api.github.com/user
user-name-attribute: id
用户信息处理
首先,在Oauth的登录流程中,我们可以通过userInfoEndpoint获得对应的OauthUser对象,也可以执行自己的一些处理,保存或者查找用户信息,因此,我们可以手动继承DefaultOAuth2UserService
import com.grtsinry43.grtblog.entity.User;
import com.grtsinry43.grtblog.mapper.UserMapper;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
/**
* @author grtsinry43
* @date 2024/11/9 23:00
* @description 热爱可抵岁月漫长
*/
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserMapper userMapper;
public CustomOAuth2UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
// 在这里,我们便可以对解析的用户做自己的操作
return oAuth2User;
}
}
自定义的用户信息处理
当我们拿到oauth2user对象后,我们便可以添加自己的相关处理逻辑
例如我们可以拿到用户的邮箱、头像、昵称等等信息,然后验证用户是否存在,不存在则创建,存在则直接查询相关信息
String provider = userRequest.getClientRegistration().getRegistrationId();
String oauthId = oAuth2User.getAttribute("sub") == null ? oAuth2User.getAttribute("id") + "" : oAuth2User.getAttribute("sub") + "";
String email = oAuth2User.getAttribute("email");
String avatar = oAuth2User.getAttribute("avatar_url") == null ? oAuth2User.getAttribute("picture") + "" : oAuth2User.getAttribute("avatar_url") + "";
User user = userMapper.getUserByOAuthProviderAndId(provider, oauthId);
if (user == null) {
user = new User();
user.setEmail(email);
user.setNickname(oAuth2User.getAttribute("login") == null ? oAuth2User.getAttribute("name") + "" : oAuth2User.getAttribute("login") + "");
user.setOauthProvider(provider);
user.setAvatar(avatar);
user.setOauthId(oauthId);
userMapper.insert(user);
} // 这里如果存在可以在选择性更新
当获取了用户信息并保证其保存至数据库时,我们就可以执行登录完成的处理啦
登录后处理
当我们登录成功后,我们可以手动继承AuthenticationSuccessHandler来实现自己的成功处理(回调)逻辑
这里我采用了jwt进行认证相关逻辑,因此我们可以采用设置cookie的方式将生成的token传递到前端,我们可以这么实现
import com.grtsinry43.grtblog.util.JwtUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @author grtsinry43
* @date 2024/11/25 19:37
* @description 热爱可抵岁月漫长
*/
@Component
public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 获取 redirect_uri 参数
String redirectUri = request.getParameter("redirect_uri");
// 如果没有指定 redirect_uri,设置默认跳转地址
if (redirectUri == null || redirectUri.isEmpty()) {
redirectUri = "https://blog.grtsinry43.com/";
}
// 获取 principal 对象
Object principal = authentication.getPrincipal();
String email;
if (principal instanceof OAuth2User) {
email = ((OAuth2User) principal).getAttribute("email");
} else if (principal instanceof LoginUserDetails) {
email = ((LoginUserDetails) principal).getUser().getEmail();
} else {
throw new ServletException("Unexpected principal type");
}
// 生成 Token
String token = JwtUtil.generateToken(email);
// 创建 Cookie 来存储 token
Cookie cookie = new Cookie("token", token);
cookie.setSecure(true); // 在生产环境中使用 HTTPS 时,启用此选项
cookie.setPath("/"); // 设置 Cookie 可在整个站点有效
cookie.setMaxAge(3600 * 24 * 7); // 设置 Cookie 的有效期为 7 天
// 将 Cookie 添加到响应中
response.addCookie(cookie);
// 重定向到指定的地址
response.sendRedirect(redirectUri);
}
}
这样我们便实现了重定向回网页和设置登录信息
前端配置
前端的配置就很简单啦,直接跳转一下网页,选择性携带一下URL编码的当前地址就可以啦
小小总结
对于Oauth2的相关流程,当前端用户点击则转到后端进行处理,换access_token和code等等,在框架拿到用户对象后便可以进行处理,结合cookie将其传到前端保存,由拦截器携带即完成了登录闭环
其实整个过程还是没啥技术含量的,但是毕竟我是主力前端的,这个还是不太了解还是有整理一下的必要的,善用轮子还是大大提高开发效率的