下面是详细讲解“springboot2.x实现oauth2授权码登陆的方法”的完整攻略:
什么是OAuth2?
OAuth2是目前最流行的用户认证和授权协议之一。它的目的是让用户可以授权第三方应用访问他们的资源,而不必将自己的用户名和密码直接提供给第三方应用。OAuth2协议有多种授权方式,其中最常用的是授权码模式。
OAuth2授权码模式流程
OAuth2授权码模式的流程分为以下步骤:
-
用户打开客户端以后,客户端要求用户给予授权,这时候用户会被要求输入用户名和密码。
-
客户端拿到用户的用户名和密码,向授权服务器申请令牌。
-
授权服务器对客户端进行认证以后,确认无误,同意发放令牌。
-
客户端使用令牌,向资源服务器申请资源。
-
资源服务器确认令牌无误,同意向客户端开放资源。
详细的授权码模式流程可以参考OAuth2协议的官方文档。
Spring Boot中实现OAuth2授权码登陆的方法
要在Spring Boot中实现OAuth2授权码登陆,需要经过以下步骤:
- 添加OAuth2依赖
在pom.xml文件中添加Spring Security OAuth2依赖:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.0</version>
</dependency>
- 配置OAuth2
在Spring Boot配置文件application.properties中添加以下配置项:
spring.security.oauth2.client.registration.google.client-id=clientId
spring.security.oauth2.client.registration.google.client-secret=clientSecret
spring.security.oauth2.client.registration.google.scope=openid,email,profile
spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/v2/auth
spring.security.oauth2.client.provider.google.token-uri=https://www.googleapis.com/oauth2/v4/token
spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo
spring.security.oauth2.client.provider.google.user-name-attribute=name
其中,配置项的具体含义可以参考Spring Security OAuth2的官方文档。
- 创建授权服务器配置
创建授权服务器配置类,指定OAuth2授权服务器的属性:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
这里采用JdbcTokenStore,将令牌保存在数据库中。客户端信息也保存在数据库中。
- 创建WebSecurityConfigurerAdapter配置
创建WebSecurityConfigurerAdapter配置类,指定安全控制的属性:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login/**","/oauth/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessURL("/index")
.permitAll()
.and()
.logout()
.permitAll();
}
}
这里定义了用于登录验证的UserDetailsService,并设置了一些权限控制。
- 创建WebMvcConfigurer配置
创建WebMvcConfigurer配置类,指定URL的处理方式:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
这里指定URL “/login” 的视图为“login”。
- 创建视图
创建登录页面视图“login.html”:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
</head>
<body>
<form th:action="@{/oauth/authorize}" method="post">
<div>
<label>Username:</label>
<input type="text" id="username" name="username" required="required"/>
</div>
<div>
<label>Password:</label>
<input type="password" id="password" name="password" required="required"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>
</body>
</html>
- 创建ResourceServerConfigurerAdapter配置
创建ResourceServerConfigurerAdapter配置类,指定需要保护的资源:
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET,"/api/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST,"/api/**").access("#oauth2.hasScope('write') and hasRole('ROLE_ADMIN')")
.antMatchers(HttpMethod.PUT,"/api/**").access("#oauth2.hasScope('write') and hasRole('ROLE_ADMIN')")
.antMatchers(HttpMethod.DELETE,"/api/**").access("#oauth2.hasScope('write') and hasRole('ROLE_ADMIN')");
}
}
这里指定了需要保护的资源,通过OAuth2授权进行访问。
至此,我们已经完成了OAuth2授权码登陆的配置。
示例
下面提供两条示例:
示例1
我们可以通过Google OAuth2服务进行认证。打开浏览器,输入地址http://localhost:8080/oauth/authorize?response_type=code&client_id=clientId&redirect_uri=https://www.baidu.com/,回车,系统会跳转到Google OAuth2服务的认证页面。输入Google账号和密码,授权成功后系统会重定向到 https://www.baidu.com/?code=authCode,其中authCode是授权码。
在获得授权码后,我们可以通过POST请求获取访问令牌:
POST /oauth/token HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0
Cache-Control: no-cache
grant_type=authorization_code&code=authCode&redirect_uri=https://www.baidu.com/
其中,Y2xpZW50SWQ6Y2xpZW50U2VjcmV0 是client ID和client secret经过Base64编码后的结果,authCode是前面获得的授权码,redirect_uri是我们前面指定的重定向地址。
POST请求的返回内容如下:
{
"access_token":"accessToken",
"token_type":"bearer",
"refresh_token":"refreshToken",
"expires_in":3599,
"scope":"openid email profile"
}
其中,accessToken是访问令牌,refreshToken是用于重新申请访问令牌的令牌,expires_in是访问令牌的过期时间(单位为秒),scope是访问令牌的范围。
我们可以使用下面的GET请求,使用访问令牌访问受保护的资源:
GET /api/hello HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Authorization: Bearer accessToken
其中,访问令牌要放在Authorization头中,Bearer是OAuth2指定的身份验证方案。
如果访问令牌有效,则会返回以下JSON格式数据:
{
"message":"Hello World!"
}
示例2
我们可以在应用程序的login视图中添加一个连接,指向http://localhost:8080/oauth/authorize。用户点击这个链接后,会跳转到OAuth2授权服务器的认证页面,进行用户认证和授权。授权完成后,系统会跳转回应用程序,并根据用户角色显示相应的页面。
首先,在应用程序的login视图中添加以下连接:
<a href="/oauth/authorize?response_type=code&client_id=clientId&redirect_uri=http://localhost:8080/login/oauth2/code/google">Login with Google</a>
其中,client_id是我们在Google Developer Console中创建应用程序时生成的客户端ID,redirect_uri是重定向地址,可以在Google Developer Console中指定。
然后,在应用程序的Controller中添加以下请求处理方法:
@GetMapping("/login/oauth2/code/google")
public String googleLogin(@RequestParam String code, Authentication authentication, Model model) {
OAuth2AccessToken accessToken = oAuth2AuthorizedClientService.loadAuthorizedClient("google", authentication.getName()).getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken.getTokenValue());
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<GoogleUserInfo> response = restTemplate.exchange("https://www.googleapis.com/oauth2/v2/userinfo", HttpMethod.GET, entity, GoogleUserInfo.class);
GoogleUserInfo userInfo = response.getBody();
if (userInfo != null) {
if (userRepository.findByEmail(userInfo.getEmail()) == null) {
User user = new User();
user.setEmail(userInfo.getEmail());
user.setUsername(userInfo.getName());
user.setRoles(Collections.singleton("ROLE_USER"));
userRepository.save(user);
}
}
User user = userRepository.findByEmail(userInfo.getEmail());
model.addAttribute("user", user);
return "index";
}
其中,我们使用OAuth2AuthorizedClientService从授权服务器请求访问令牌,使用RestTemplate访问资源服务器的API获得用户信息,通过userRepository保存用户信息,最后将用户信息添加到视图。
下面是GoogleUserInfo的实现:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GoogleUserInfo {
private String id;
private String email;
private String name;
}
最后,在应用程序的控制器中添加以下授权检查逻辑:
@GetMapping("/api/hello")
public Map<String, String> hello(Authentication authentication) {
OAuth2Authentication oauth = (OAuth2Authentication) authentication;
Map<String, String> map = new HashMap<>();
map.put("message", "Hello World! You have " + oauth.getAuthorities().toString());
return map;
}
其中,oauth.getAuthorities()可以获取用户的角色信息。
至此,我们完成了使用Google OAuth2服务进行OAuth2授权码登陆的过程。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:springboot2.x实现oauth2授权码登陆的方法 - Python技术站