如何从java.security.Principal获取全部属性?Spring Security 5 OAuth2场景
嘿,我来帮你解决这个问题!你说得对,Spring Security确实提供了直接获取OAuth2用户详情的方法,根本不用手动解析JSON。让我一步步给你讲清楚:
首先,你当前返回的Principal其实是OAuth2AuthenticationToken类型,它内部包含了一个OAuth2User对象,这个对象里就存储着所有从OAuth提供商(Google、Facebook、GitHub)返回的用户属性。
方法一:直接转换Principal为OAuth2AuthenticationToken
修改你的Controller,把Principal参数换成OAuth2AuthenticationToken,或者在方法里进行强制转换,然后获取OAuth2User来提取信息:
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Map; @Controller public class HomeController { @GetMapping({"", "/"}) @ResponseBody public String getHome(OAuth2AuthenticationToken authentication) { // 获取OAuth2用户对象 OAuth2User oAuth2User = authentication.getPrincipal(); // 提取通用属性(不同提供商的属性键可能略有不同) String username = oAuth2User.getAttribute("name"); // Google/Facebook用name,GitHub用login String email = oAuth2User.getAttribute("email"); // 也可以获取所有属性的Map Map<String, Object> allAttributes = oAuth2User.getAttributes(); return String.format("用户名: %s<br>邮箱: %s<br>所有用户属性: %s", username, email, allAttributes); } }
方法二:使用@AuthenticationPrincipal注解
如果你不想依赖OAuth2AuthenticationToken,可以直接用@AuthenticationPrincipal注解注入OAuth2User,这样代码更简洁:
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HomeController { @GetMapping({"", "/"}) @ResponseBody public String getHome(@AuthenticationPrincipal OAuth2User oAuth2User) { String username = oAuth2User.getAttribute("name"); String email = oAuth2User.getAttribute("email"); return "用户名: " + username + "<br>邮箱: " + email; } }
注意:不同OAuth提供商的属性键差异
不同的平台返回的属性键可能不一样,比如:
- Google:
name(用户名)、email(邮箱)、picture(头像) - GitHub:
login(用户名)、email(邮箱,需要额外权限)、avatar_url(头像) - Facebook:
name(用户名)、email(邮箱)、picture(头像)
如果想统一处理不同平台的用户信息,可以自定义OAuth2UserService来封装成统一的用户对象:
方法三:自定义OAuth2UserService统一用户信息
- 先创建一个自定义的用户类(比如
CustomOAuth2User),实现OAuth2User接口,用来统一存储用户信息:
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.core.user.OAuth2User; import java.util.Collection; import java.util.Map; public class CustomOAuth2User implements OAuth2User { private String username; private String email; private Map<String, Object> attributes; private Collection<? extends GrantedAuthority> authorities; public CustomOAuth2User(String username, String email, Map<String, Object> attributes, Collection<? extends GrantedAuthority> authorities) { this.username = username; this.email = email; this.attributes = attributes; this.authorities = authorities; } // 自定义获取方法 public String getUsername() { return username; } public String getEmail() { return email; } // 实现OAuth2User接口的方法 @Override public Map<String, Object> getAttributes() { return attributes; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getName() { return username; } }
- 然后创建自定义的
OAuth2UserService,继承DefaultOAuth2UserService,根据不同的提供商处理用户信息:
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; @Service public class CustomOAuth2UserService extends DefaultOAuth2UserService { @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2User oAuth2User = super.loadUser(userRequest); String registrationId = userRequest.getClientRegistration().getRegistrationId(); String username; String email; // 根据不同提供商处理属性 switch (registrationId) { case "google": username = oAuth2User.getAttribute("name"); email = oAuth2User.getAttribute("email"); break; case "github": username = oAuth2User.getAttribute("login"); email = oAuth2User.getAttribute("email"); // GitHub需要用户授权邮箱权限 break; case "facebook": username = oAuth2User.getAttribute("name"); email = oAuth2User.getAttribute("email"); break; default: throw new IllegalArgumentException("不支持的OAuth提供商: " + registrationId); } // 返回自定义的OAuth2User对象 return new CustomOAuth2User(username, email, oAuth2User.getAttributes(), oAuth2User.getAuthorities()); } }
- 在SecurityConfig中配置这个自定义的UserService:
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { private final CustomOAuth2UserService customOAuth2UserService; // 构造注入自定义UserService public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) { this.customOAuth2UserService = customOAuth2UserService; } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and().oauth2Login() .userInfoEndpoint() .userService(customOAuth2UserService); // 配置自定义的用户服务 } }
- 最后在Controller中直接注入自定义的
CustomOAuth2User:
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HomeController { @GetMapping({"", "/"}) @ResponseBody public String getHome(@AuthenticationPrincipal CustomOAuth2User customUser) { return "统一用户名: " + customUser.getUsername() + "<br>统一邮箱: " + customUser.getEmail(); } }
这样不管用哪个OAuth提供商登录,你都能通过统一的方法获取用户信息,不用再关心不同平台的属性差异啦!
内容的提问来源于stack exchange,提问作者Taras
相关产品推荐
相关产品推荐

