Spring Boot Security OAuth2 JWT单点登录完整示例
时间: 2024-02-22 21:47:19 浏览: 289
Spring Boot Security OAuth2 JWT 单点登录是一个非常实用的功能。下面是一个完整的示例,帮助你快速上手。
首先,我们需要在 pom.xml 文件中添加以下依赖:
```xml
<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>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
```
然后,我们需要创建一个名为 `SecurityConfig` 的类,继承 `WebSecurityConfigurerAdapter` 类,并重写 `configure` 方法。在这个方法中,我们可以配置 Spring Security 的一些基本设置,如登录页面、用户认证等。
```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private CustomOAuth2UserService oAuth2UserService;
@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.authorizeRequests()
.antMatchers("/", "/error", "/webjars/**").permitAll()
.antMatchers("/oauth2/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(oAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
}
@Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
@Bean
public JwtEncoder jwtEncoder() {
return new JwtEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
```
接下来,我们需要创建一个名为 `CustomUserDetailsService` 的类,实现 `UserDetailsService` 接口,并重写 `loadUserByUsername` 方法。在这个方法中,我们可以实现自己的用户认证逻辑。
```java
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username : " + username));
return UserPrincipal.create(user);
}
@Transactional
public UserDetails loadUserById(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new ResourceNotFoundException("User", "id", id)
);
return UserPrincipal.create(user);
}
}
```
然后,我们需要创建一个名为 `CustomOAuth2UserService` 的类,实现 `OAuth2UserService` 接口,并重写 `loadUser` 方法。在这个方法中,我们可以实现自己的第三方登录逻辑,比如从 Facebook 或 Google 获取用户信息。
```java
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
try {
return processOAuth2User(oAuth2UserRequest, oAuth2User);
} catch (Exception ex) {
throw new OAuth2AuthenticationException(ex.getMessage(), ex);
}
}
private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(
oAuth2UserRequest.getClientRegistration().getRegistrationId(),
oAuth2User.getAttributes()
);
if(StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
throw new OAuth2AuthenticationProcessingException("Email not found from OAuth2 provider");
}
Optional<User> userOptional = userRepository.findByEmail(oAuth2UserInfo.getEmail());
User user;
if(userOptional.isPresent()) {
user = userOptional.get();
if(!user.getProvider().equals(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()))) {
throw new OAuth2AuthenticationProcessingException("Looks like you're signed up with " +
user.getProvider() + " account. Please use your " + user.getProvider() +
" account to login.");
}
user = updateExistingUser(user, oAuth2UserInfo);
} else {
user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo);
}
return UserPrincipal.create(user, oAuth2User.getAttributes());
}
private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
User user = new User();
user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
user.setProviderId(oAuth2UserInfo.getId());
user.setUsername(oAuth2UserInfo.getName());
user.setEmail(oAuth2UserInfo.getEmail());
user.setImageUrl(oAuth2UserInfo.getImageUrl());
return userRepository.save(user);
}
private User updateExistingUser(User existingUser, OAuth2UserInfo oAuth2UserInfo) {
existingUser.setUsername(oAuth2UserInfo.getName());
existingUser.setImageUrl(oAuth2UserInfo.getImageUrl());
return userRepository.save(existingUser);
}
}
```
接着,我们需要创建一个名为 `OAuth2UserInfo` 的接口,定义一些获取第三方登录用户信息的方法。
```java
public interface OAuth2UserInfo {
String getId();
String getName();
String getEmail();
String getImageUrl();
}
```
然后,我们需要创建一个名为 `OAuth2UserInfoFactory` 的工厂类,根据不同的第三方登录平台,返回不同的实现类。
```java
public class OAuth2UserInfoFactory {
public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map<String, Object> attributes) {
if(registrationId.equalsIgnoreCase(AuthProvider.google.toString())) {
return new GoogleOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(AuthProvider.facebook.toString())) {
return new FacebookOAuth2UserInfo(attributes);
} else {
throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet.");
}
}
}
```
最后,我们需要创建一个名为 `JwtEncoder` 的类,实现 `TokenEnhancer` 接口,并重写 `enhance` 方法。在这个方法中,我们可以创建一个 JWT Token,并将用户信息存储在 Token 中。
```java
public class JwtEncoder implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("id", user.getId());
additionalInfo.put("username", user.getUsername());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
```
至此,我们的 Spring Boot Security OAuth2 JWT 单点登录示例就完成了。
阅读全文