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

yizhihongxing

下面是详细讲解“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日

相关文章

  • java static块和构造函数的实例详解

    Java中的static块和构造函数都是用来初始化类的成员变量的,但两者有着不同的特点和应用场景。下面详细讲解static块和构造函数的用法及其区别。 一、static块 1.1 定义 在Java中,static块是一个静态代码块,用来初始化静态成员变量。在类加载时,如果类中有static块,则首先会执行static块,然后才会执行其他代码块和构造函数。 1…

    Java 2023年5月26日
    00
  • Android图片的Base64编码与解码及解码Base64图片方法

    针对这个话题,以下是详细讲解“Android图片的Base64编码与解码及解码Base64图片方法”的完整攻略。 什么是Base64编码 Base64编码是一种用于将二进制数据转为文本数据的编码方式,主要用途是将数据在网络上进行传输,例如在网页中展示图片等。 如何在Android中进行Base64编码 在Android中,我们可以使用Base64类进行Bas…

    Java 2023年5月20日
    00
  • 微信小程序template模板与component组件的区别和使用详解

    微信小程序template模板与component组件的区别和使用详解 在微信小程序开发过程中,template和component是两个经常用到的概念。它们可以用来复用一些公共的代码和样式,也能使代码更加简洁易读。本文将详细讲解template模板和component组件的相关概念、特点、用法以及注意事项,并通过示例代码进行说明和实践。 template模…

    Java 2023年5月23日
    00
  • 详解Java如何优雅的使用装饰器模式

    下面来详细讲解“详解Java如何优雅的使用装饰器模式”的完整攻略。 装饰器模式简介 装饰器模式(Decorator Pattern)是一种常用的设计模式,它允许将对象的行为在运行时更改,而无需修改其结构。这种模式是在不必改变原有对象的基础上,动态地给一个对象增加一些额外的职责。 如何使用装饰器模式 使用装饰器模式一般是通过创建一个抽象装饰者,然后通过继承该装…

    Java 2023年5月26日
    00
  • 详解Spring中Bean的生命周期和作用域及实现方式

    详解Spring中Bean的生命周期和作用域及实现方式 Bean的生命周期 Bean的生命周期包含以下几个阶段: 实例化阶段:Spring通过反射机制或者工厂方法等方式创建Bean实例。 设置属性值阶段:Spring将Bean实例化后,通过调用setter方法或者直接设置字段值的方式,将Bean所需的属性注入进去。 初始化阶段:Bean的初始化可以分为两种方…

    Java 2023年5月31日
    00
  • SpringMVC框架如何与Junit整合看这个就够了

    SpringMVC框架如何与Junit整合 本文将详细讲解如何使用Junit测试SpringMVC框架,并提供两个示例说明。 环境准备 在开始整合Junit和SpringMVC框架之前,我们需要准备以下环境: JDK 18或以上版本 Maven 3.6.3或以上版本 Tomcat 9.0或以上版本 Junit 5.7.2或以上版本 实现步骤 下面是整合Jun…

    Java 2023年5月17日
    00
  • 学习使用Android Chronometer计时器

    学习使用 Android Chronometer 计时器的完整攻略如下: 1. 什么是 Android Chronometer 计时器? Android Chronometer 计时器是 Android 中的一个可视化组件,它可以通过界面上直观的数字和符号帮助用户简单直观地了解时间的流逝。Chronometer 计时器可以用于记录运动时间、考试时间等需要计时…

    Java 2023年5月26日
    00
  • SpringBoot创建多模块项目的全过程记录

    我将为您详细讲解如何使用SpringBoot创建多模块项目的全过程记录。创建多模块项目有很多好处,例如可以将不同的功能模块独立开发、测试和维护,增加代码的可读性和可维护性。下面是创建多模块项目的步骤: 1. 创建maven的多模块工程 使用Maven创建一个新的多模块项目,一个工程包含多个子模块。在项目的根目录下,使用以下Maven命令创建一个多模块项目: …

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