本文目录导读:

- 使用Spring Security OAuth2(推荐)
- 自定义OAuth2客户端实现
- 使用第三方OAuth2登录(如Google)
- 使用JWT实现OAuth2令牌
- 完整的OAuth2流程示例
- 配置示例(application.yml)
- 安全建议
- 注意事项
我来详细介绍Java中实现OAuth2的几种常见方式:
使用Spring Security OAuth2(推荐)
添加依赖(Spring Boot)
<!-- Spring Boot 2.x -->
<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>
</dependency>
配置授权服务器
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client-id")
.secret(passwordEncoder.encode("client-secret"))
.authorizedGrantTypes("authorization_code", "password", "refresh_token")
.scopes("read", "write")
.redirectUris("http://localhost:8080/callback")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(86400);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.approvalStoreDisabled(false);
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
配置资源服务器
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.csrf().disable();
}
}
自定义OAuth2客户端实现
@Service
public class OAuth2ClientService {
private final RestTemplate restTemplate;
@Value("${oauth2.client.id}")
private String clientId;
@Value("${oauth2.client.secret}")
private String clientSecret;
@Value("${oauth2.token.url}")
private String tokenUrl;
public OAuth2ClientService() {
this.restTemplate = new RestTemplate();
}
// 获取授权码URL
public String getAuthorizationUrl(String redirectUri, String state) {
return "https://oauth-server.com/oauth/authorize?" +
"response_type=code" +
"&client_id=" + clientId +
"&redirect_uri=" + redirectUri +
"&state=" + state;
}
// 交换授权码获取访问令牌
public OAuth2Token exchangeCodeForToken(String code, String redirectUri) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("code", code);
params.add("redirect_uri", redirectUri);
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<>(params, headers);
ResponseEntity<OAuth2Token> response = restTemplate.postForEntity(
tokenUrl, request, OAuth2Token.class);
return response.getBody();
}
// 刷新令牌
public OAuth2Token refreshToken(String refreshToken) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "refresh_token");
params.add("refresh_token", refreshToken);
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<>(params, headers);
ResponseEntity<OAuth2Token> response = restTemplate.postForEntity(
tokenUrl, request, OAuth2Token.class);
return response.getBody();
}
}
@Data
public class OAuth2Token {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("expires_in")
private int expiresIn;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("scope")
private String scope;
}
使用第三方OAuth2登录(如Google)
@Controller
public class OAuth2LoginController {
@Autowired
private OAuth2ClientService oAuth2ClientService;
// 重定向到OAuth2提供商
@GetMapping("/login/google")
public String loginWithGoogle(HttpSession session) {
String state = UUID.randomUUID().toString();
session.setAttribute("oauth_state", state);
String redirectUri = "http://localhost:8080/callback/google";
String authUrl = oAuth2ClientService.getAuthorizationUrl(redirectUri, state);
return "redirect:" + authUrl;
}
// 处理回调
@GetMapping("/callback/google")
public String handleGoogleCallback(
@RequestParam("code") String code,
@RequestParam("state") String state,
HttpSession session) {
// 验证state防止CSRF
String savedState = (String) session.getAttribute("oauth_state");
if (!state.equals(savedState)) {
return "error";
}
// 交换令牌
String redirectUri = "http://localhost:8080/callback/google";
OAuth2Token token = oAuth2ClientService.exchangeCodeForToken(code, redirectUri);
// 保存令牌到session
session.setAttribute("access_token", token.getAccessToken());
session.setAttribute("refresh_token", token.getRefreshToken());
return "redirect:/dashboard";
}
}
使用JWT实现OAuth2令牌
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpiration;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder()
.setSubject(userPrincipal.getId())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
// 无效签名
} catch (MalformedJwtException ex) {
// 无效令牌
} catch (ExpiredJwtException ex) {
// 令牌过期
} catch (UnsupportedJwtException ex) {
// 不支持的令牌
} catch (IllegalArgumentException ex) {
// 空令牌
}
return false;
}
}
完整的OAuth2流程示例
@RestController
@RequestMapping("/api/oauth2")
public class OAuth2Controller {
@Autowired
private OAuth2Service oAuth2Service;
// 获取授权码
@PostMapping("/authorize")
public ResponseEntity<AuthorizationResponse> authorize(
@RequestBody AuthorizationRequest request) {
String authCode = oAuth2Service.generateAuthorizationCode(
request.getClientId(),
request.getRedirectUri(),
request.getScope());
return ResponseEntity.ok(new AuthorizationResponse(authCode));
}
// 获取访问令牌
@PostMapping("/token")
public ResponseEntity<OAuth2Token> getToken(
@RequestBody TokenRequest request) {
OAuth2Token token = oAuth2Service.generateToken(
request.getGrantType(),
request.getCode(),
request.getRedirectUri(),
request.getClientId(),
request.getClientSecret());
return ResponseEntity.ok(token);
}
// 验证令牌
@GetMapping("/verify")
public ResponseEntity<TokenInfo> verifyToken(
@RequestHeader("Authorization") String authHeader) {
String token = authHeader.replace("Bearer ", "");
TokenInfo tokenInfo = oAuth2Service.verifyToken(token);
return ResponseEntity.ok(tokenInfo);
}
// 撤销令牌
@PostMapping("/revoke")
public ResponseEntity<Void> revokeToken(
@RequestParam("token") String token) {
oAuth2Service.revokeToken(token);
return ResponseEntity.ok().build();
}
}
配置示例(application.yml)
oauth2:
client:
id: your-client-id
secret: your-client-secret
redirect-uri: http://localhost:8080/callback
server:
authorization-url: https://oauth-server.com/oauth/authorize
token-url: https://oauth-server.com/oauth/token
user-info-url: https://oauth-server.com/userinfo
jwt:
secret: your-jwt-secret-key
expiration: 86400000 # 24小时
安全建议
@Component
public class OAuth2SecurityUtils {
// 验证回调URL
public boolean isValidRedirectUri(String redirectUri) {
// 白名单验证
List<String> allowedUris = Arrays.asList(
"http://localhost:8080/callback",
"https://yourdomain.com/callback"
);
return allowedUris.contains(redirectUri);
}
// 生成安全的state参数
public String generateSecureState() {
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// 验证令牌作用域
public boolean hasScope(String token, String requiredScope) {
// 实现作用域验证逻辑
return true;
}
}
注意事项
- 使用HTTPS:所有OAuth2通信都应使用HTTPS
- 安全存储客户端密钥:不要硬编码,使用配置服务或环境变量
- 设置合理的令牌过期时间:access token短时效,refresh token可配置更长
- 实施CSRF保护:使用state参数
- 日志记录:记录认证失败尝试
- 速率限制:防止暴力破解
选择哪种实现方式取决于你的具体需求:
- Spring Security OAuth2:适合Spring Boot应用
- 自定义实现:需要更多控制时
- 第三方OAuth2:集成社交登录
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播