springboot2.x实现oauth2授权码登陆的方法

下面是详细讲解“springboot2.x实现oauth2授权码登陆的方法”的完整攻略:

什么是OAuth2?

OAuth2是目前最流行的用户认证和授权协议之一。它的目的是让用户可以授权第三方应用访问他们的资源,而不必将自己的用户名和密码直接提供给第三方应用。OAuth2协议有多种授权方式,其中最常用的是授权码模式。

OAuth2授权码模式流程

OAuth2授权码模式的流程分为以下步骤:

  1. 用户打开客户端以后,客户端要求用户给予授权,这时候用户会被要求输入用户名和密码。

  2. 客户端拿到用户的用户名和密码,向授权服务器申请令牌。

  3. 授权服务器对客户端进行认证以后,确认无误,同意发放令牌。

  4. 客户端使用令牌,向资源服务器申请资源。

  5. 资源服务器确认令牌无误,同意向客户端开放资源。

详细的授权码模式流程可以参考OAuth2协议的官方文档。

Spring Boot中实现OAuth2授权码登陆的方法

要在Spring Boot中实现OAuth2授权码登陆,需要经过以下步骤:

  1. 添加OAuth2依赖

在pom.xml文件中添加Spring Security OAuth2依赖:

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.4.0</version>
</dependency>
  1. 配置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的官方文档。

  1. 创建授权服务器配置

创建授权服务器配置类,指定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,将令牌保存在数据库中。客户端信息也保存在数据库中。

  1. 创建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,并设置了一些权限控制。

  1. 创建WebMvcConfigurer配置

创建WebMvcConfigurer配置类,指定URL的处理方式:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
}

这里指定URL “/login” 的视图为“login”。

  1. 创建视图

创建登录页面视图“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>
  1. 创建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技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • jsp的常用指令有哪些(编译指令/动作指令整理)

    下面是关于JSP的常用指令的详细讲解。 JSP指令简介 JSP指令是JSP文件中特殊的语句,用于向JSP容器提供特殊的指令或提示,以帮助容器编译JSP页面。JSP指令有两种类型,分别是编译指令和动作指令。 编译指令 编译指令告诉JSP引擎如何处理JSP页面。编译指令必须放在JSP页面的第一行,并且必须以“%@”开&#…

    Java 2023年6月15日
    00
  • IIS和tomcat5多站点配置流程

    针对你提出的问题,“IIS和tomcat5多站点配置流程”的完整攻略,以下是步骤和示例: 1. 配置IIS IIS是Windows操作系统默认带的Web服务器,它可以作为一个反向代理服务器,把所有请求转发到Tomcat服务器。下面介绍如何配置IIS,使其可以代理多个Tomcat站点。 1.1 安装IIS 在Windows服务器上打开“服务器管理器”,选择“添…

    Java 2023年5月19日
    00
  • 使用ByteArrayOutputStream实现将数据写入本地文件

    使用ByteArrayOutputStream实现将数据写入本地文件的攻略如下: 步骤一:导入相关类库和创建变量 import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.…

    Java 2023年5月26日
    00
  • 详解Java中用于国际化的locale类

    详解Java中用于国际化的Locale类 Locale类是Java用于处理地域性信息的一个重要类,在Java中,通常用来做国际化和本地化。 什么是Locale类? Locale是一个Java类,它代表一个特定的地域、文化和语言环境。它包含了一些常见的属性,如语言、国家和地区等。Locale类提供了一种标准的方式来处理本地化和国际化内容。 如何使用Locale…

    Java 2023年5月26日
    00
  • Spring Boot 中密码加密的两种方法

    下面是关于Spring Boot中密码加密的两种方法的完整攻略。 1、使用BCryptPasswordEncoder 1.1 添加依赖 在pom.xml文件中添加如下依赖,用于使用BCryptPasswordEncoder加密密码: <dependency> <groupId>org.springframework.security&…

    Java 2023年5月20日
    00
  • 探讨Java中最常见的十道面试题(超经典)

    让我来为你详细讲解“探讨Java中最常见的十道面试题(超经典)”的完整攻略。 前言 在面试Java相关职位时,经常会被问到一些非常经典的问题。本文将列举出Java中最常见的十道面试题,并为每个问题提供完整的解答,希望能够帮助你在面试时取得更好的成绩。 面试题1:Java中的“值传递”和“引用传递”有何区别? 在Java中,所有的参数传递都是“值传递”,也就是…

    Java 2023年5月24日
    00
  • Java 中的正则表达式单字符预定义字符匹配问题

    Java 中的正则表达式是一种用来匹配字符串的工具,它使用特殊的语法规则,允许我们定义一个特定模式的字符串,并且可以在其他字符串中找到符合该模式的文本。 在 Java 中,正则表达式中包含了许多“预定义字符”,用于表示单个字符的特定类型或属性。下面是一些常见的预定义字符: . 表示任何单个字符。 \d 表示任何数字。 \D 表示任何非数字字符。 \s 表示任…

    Java 2023年5月27日
    00
  • java实现银行家算法(Swing界面)

    Java实现银行家算法(Swing界面)攻略 银行家算法(Banker’s Algorithm)是一种经典的死锁预防算法,常用于操作系统中。在多进程环境下,进程需要占用资源,但是资源并不足够,如果资源分配策略不合理,则可能会出现死锁的情况。银行家算法通过资源的最大需求量和已分配需求量来判断分配资源是否会导致死锁的发生,从而保障系统运行的安全性。 本文基于Ja…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部