我的主要目标是在用google登录后存储每个用户的客户端ID。这个github repo包含了我现在需要的大部分内容。关注的两个主要文件是OAuthSecurityConfig.java和UserRestController.java。
当我导航到/user
时,Principal包含我需要的所有详细信息。因此,我可以使用以下代码段来获取我需要的数据:
Authentication a = SecurityContextHolder.getContext().getAuthentication();
String clientId = ((OAuth2Authentication) a).getOAuth2Request().getClientId();
然后我可以将clientId存储在repo中
User user = new User(clientId);
userRepository.save(user);
这个问题是用户不必导航到/user
。因此,人们可以在没有注册的情况下导航到/score/user1
。
这个API将来是一个Android应用程序的后端,所以jquery重定向到/user
将是不安全的,不会工作。
我尝试过的事情:
尝试1
我创建了以下类:
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
}
return new UserRepositoryUserDetails(user);
}
}
并覆盖WebSecurityConfigurerAdapter
with:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
当用户登录时,不会调用两个重写的方法(我使用System.out.println
检查)
尝试2
我尝试添加.userDetailsService(customUserDetailsService)
至:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// Starts authorizing configurations.
.authorizeRequests()
// Do not require auth for the "/" and "/index.html" URLs
.antMatchers("/", "/**.html", "/**.js").permitAll()
// Authenticate all remaining URLs.
.anyRequest().fullyAuthenticated()
.and()
.userDetailsService(customUserDetailsService)
// Setting the logout URL "/logout" - default logout URL.
.logout()
// After successful logout the application will redirect to "/" path.
.logoutSuccessUrl("/")
.permitAll()
.and()
// Setting the filter for the URL "/google/login".
.addFilterAt(filter(), BasicAuthenticationFilter.class)
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
这两种方法仍然没有调用,我觉得我更接近解决方案。任何帮助将不胜感激。
如果有其他人坚持这个,我的解决方案是创建一个从OAuth2ClientAuthenticationProcessingFilter
扩展的自定义类,然后覆盖successfulAuthentication
方法以获取用户身份验证详细信息并将其保存到我的数据库。
示例(kotlin):
在你的ssoFilter方法(如果你按照本教程qazxsw poi)或任何你用来注册你的ouath客户端,改变使用
https://spring.io/guides/tutorials/spring-boot-oauth2
为您的自定义类
val googleFilter = Auth2ClientAuthenticationProcessingFilter("/login/google");
当然还要声明CustomAuthProcessingFilter类
val googleFilter = CustomAuthProcessingFilter("login/google")
这里的方法是提供自定义OidcUserService并覆盖loadUser()方法,因为Google登录基于OpenId Connect。
首先定义一个模型类来保存提取的数据,如下所示:
class CustomAuthProcessingFilter(defaultFilterProcessesUrl: String?)
: OAuth2ClientAuthenticationProcessingFilter(defaultFilterProcessesUrl) {
override fun successfulAuthentication(request: HttpServletRequest?, response: HttpServletResponse?, chain: FilterChain?, authResult: Authentication?) {
super.successfulAuthentication(request, response, chain, authResult)
// Check if user is authenticated.
if (authResult === null || !authResult.isAuthenticated) {
return
}
// Use userDetails to grab the values you need like socialId, email, userName, etc...
val userDetails: LinkedHashMap<*, *> = userAuthentication.details as LinkedHashMap<*, *>
}
}
然后使用loadUser()方法创建自定义OidcUserService,该方法首先调用提供的框架实现,然后添加自己的逻辑以持久保存所需的用户数据,如下所示:
public class GoogleUserInfo {
private Map<String, Object> attributes;
public GoogleUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
public String getId() {
return (String) attributes.get("sub");
}
public String getName() {
return (String) attributes.get("name");
}
public String getEmail() {
return (String) attributes.get("email");
}
}
并在安全配置类中注册自定义OidcUserService:
@Service
public class CustomOidcUserService extends OidcUserService {
@Autowired
private UserRepository userRepository;
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);
try {
return processOidcUser(userRequest, oidcUser);
} catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause());
}
}
private OidcUser processOidcUser(OidcUserRequest userRequest, OidcUser oidcUser) {
GoogleUserInfo googleUserInfo = new GoogleUserInfo(oidcUser.getAttributes());
// see what other data from userRequest or oidcUser you need
Optional<User> userOptional = userRepository.findByEmail(googleUserInfo.getEmail());
if (!userOptional.isPresent()) {
User user = new User();
user.setEmail(googleUserInfo.getEmail());
user.setName(googleUserInfo.getName());
// set other needed data
userRepository.save(user);
}
return oidcUser;
}
}
模式详细说明可以在文档中找到:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomOidcUserService customOidcUserService;
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(customOidcUserService);
}
}
AuthenticationSuccessEvent