分布式oauth2单点登录 🗺️
- 1. 创建 Authorization Server
- 1.1 添加依赖
- 1.2 创建用户实体类
- 1.3 创建 UserRepository 接口
- 1.4 创建 UserDetailsService 实现类
- 1.5 创建 WebSecurityConfig 配置类
- 1.6 创建 LoginController
- 1.7 创建登录页面
- 1.8 配置授权服务器
- 2. 创建 Resource Server
- 2.1 添加依赖
- 2.2 创建 Resource Server 配置类
- 2.3 创建受保护资源的 API
- 2.4 创建主应用类
- 2.5 配置应用端口和上下文路径(可选)
- 3. 创建 Client 应用
- 3.1 添加依赖
- 3.2 创建 SecurityConfig 配置类
- 3.3 配置 application.yml 文件
- 3.4 创建主应用类
- 3.5 创建 HomeController 控制器
- 3.6 创建主页模板
- 3.7 创建 ResourceController 控制器
- 3.8 创建受保护资源页面模板
原来,成全别人,是要有一点痛苦的
为了使用 Spring Cloud、Spring Cloud Security 和 OAuth2 实现单点登录,你需要完成以下步骤:
- 创建 Authorization Server
- 创建 Resource Server
- 创建 Client 应用
1. 创建 Authorization Server
好的,让我们详细介绍如何创建认证服务器,并实现登录页面。
1.1 添加依赖
首先,在你的 Maven pom.xml
文件中添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
1.2 创建用户实体类
在认证服务器中,创建一个用户实体类 User
。该实体类将表示用户在数据库中的信息。
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
private boolean enabled;
// 省略 getter 和 setter
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
}
1.3 创建 UserRepository 接口
创建一个 UserRepository
接口,该接口继承自 JpaRepository
。我们将使用此接口查询数据库中的用户信息。
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
1.4 创建 UserDetailsService 实现类
接下来,创建一个名为 UserDetailsServiceImpl
的类,该类实现了 UserDetailsService
接口。我们将使用这个类加载数据库中的用户信息。
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return user;
}
}
1.5 创建 WebSecurityConfig 配置类
创建一个名为 WebSecurityConfig
的配置类,该类继承自 WebSecurityConfigurerAdapter
。我们将在这个类中配置安全相关的设置。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/css/", "/js/", "/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
1.6 创建 LoginController
现在,我们将创建一个名为 LoginController
的控制器类,用于处理登录页面的请求。
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
}
1.7 创建登录页面
在 src/main/resources/templates
目录下创建一个名为 login.html
的登录页面。这个页面将包含一个表单,用于输入用户名和密码。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登录</title>
<link rel="stylesheet" th:href="@{/css/login.css}" />
</head>
<body>
<div class="login-container">
<h2>登录</h2>
<form th:action="@{/login}" method="post">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" class="form-control" required autofocus />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" class="form-control" required />
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</body>
</html>
1.8 配置授权服务器
现在,我们需要将认证服务器配置为 OAuth2 授权服务器。在上面创建的 AuthorizationServerConfig
类中,确保已经添加了以下内容:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource)
.withClient("client-id")
.secret(passwordEncoder().encode("client-secret"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read", "write")
.autoApprove(true)
.redirectUris("http://localhost:8082/login");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
现在,你已经成功创建了一个认证服务器,并实现了登录页面。启动应用后,访问 http://localhost:8080/login
即可看到登录页面。登录成功后,客户端应用将被授权访问受保护的资源。
2. 创建 Resource Server
接下来我们将详细介绍如何创建资源服务器。资源服务器将托管受保护的资源,并确保只有授权的客户端才能访问这些资源。
2.1 添加依赖
首先,在你的 Maven pom.xml
文件中添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
</dependencies>
2.2 创建 Resource Server 配置类
接下来,创建一个名为 ResourceServerConfig
的配置类。我们将在这个类中配置资源服务器相关的设置。
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
2.3 创建受保护资源的 API
现在,我们将创建一个名为 ProtectedResourceController
的控制器类,用于处理受保护资源的请求。
@RestController
@RequestMapping("/api")
public class ProtectedResourceController {
@GetMapping("/userinfo")
public Map<String, Object> getUserInfo(@AuthenticationPrincipal OAuth2Authentication authentication) {
return (Map<String, Object>) authentication.getUserAuthentication().getDetails();
}
@GetMapping("/hello")
public String hello() {
return "Hello, this is a protected resource.";
}
}
2.4 创建主应用类
创建一个名为 ResourceServerApplication
的主应用类,用于启动资源服务器。
@SpringBootApplication
public class ResourceServerApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerApplication.class, args);
}
}
2.5 配置应用端口和上下文路径(可选)
在 src/main/resources
目录下创建一个名为 application.yml
的配置文件。在这个文件中,我们可以设置应用的端口和上下文路径。这一步是可选的,如果你不进行配置,应用将使用默认端口 8080 和根上下文路径。
server:
port: 8081
servlet:
context-path: /resource-server
现在,你已经创建了一个资源服务器,它包含一个受保护的 API。要启动应用,请运行 ResourceServerApplication
主类。启动后,资源服务器将监听在 http://localhost:8081/resource-server/api
(或者在你自定义的端口和上下文路径)。
只有授权的客户端才能访问受保护的资源。客户端需要使用在认证服务器获取的访问令牌来访问这些资源。
3. 创建 Client 应用
客户端应用将使用 OAuth2 进行授权,并通过资源服务器访问受保护的资源。
3.1 添加依赖
首先,在你的 Maven pom.xml
文件中添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
</dependencies>
3.2 创建 SecurityConfig 配置类
创建一个名为 SecurityConfig
的配置类,该类继承自 WebSecurityConfigurerAdapter
。我们将在这个类中配置客户端应用的安全设置,以及 OAuth2 相关的配置。
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"));
}
}
3.3 配置 application.yml 文件
在 src/main/resources
目录下创建一个名为 application.yml
的配置文件。在这个文件中,我们将设置 OAuth2 客户端的相关配置。
security:
oauth2:
client:
clientId: client-id
clientSecret: client-secret
accessTokenUri: http://localhost:8080/oauth/token
userAuthorizationUri: http://localhost:8080/oauth/authorize
scope:
- read
- write
resource:
userInfoUri: http://localhost:8081/api/userinfo
确保使用你在认证服务器上注册的 clientId
和 clientSecret
,并将 accessTokenUri
和 userAuthorizationUri
设置为认证服务器的相应端点。userInfoUri
应该指向资源服务器上的受保护资源。
3.4 创建主应用类
创建一个名为 ClientApplication
的主应用类,用于启动客户端应用。
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
3.5 创建 HomeController 控制器
创建一个名为 HomeController
的控制器类,用于处理客户端应用的主页请求。
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model, OAuth2AuthenticationToken authentication) {
model.addAttribute("userName", authentication.getPrincipal().getAttribute("name"));
return "home";
}
}
3.6 创建主页模板
在 src/main/resources/templates
目录下创建一个名为 home.html
的主页模板。这个页面将显示登录用户的名字和一个链接,用于访问受保护的资源。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>主页</title>
<link rel="stylesheet" th:href="@{/css/home.css}" />
</head>
<body>
<div class="home-container">
<h2>Welcome, <span th:text="${userName}">User</span>!</h2>
<p>
<a href="/protected-resource">访问受保护资源</a>
</p>
</div>
</body>
</html>
3.7 创建 ResourceController 控制器
创建一个名为 ResourceController
的控制器类,用于访问资源服务器上的受保护资源。这个类将使用 OAuth2RestTemplate
来携带访问令牌并发起请求。
@Controller
public class ResourceController {
@Autowired
private OAuth2RestTemplate restTemplate;
@GetMapping("/protected-resource")
public String getProtectedResource(Model model) {
ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:8081/resource-server/api/hello", String.class);
model.addAttribute("resource", response.getBody());
return "protectedResource";
}
}
3.8 创建受保护资源页面模板
在 src/main/resources/templates
目录下创建一个名为 protectedResource.html
的受保护资源页面模板。这个页面将显示从资源服务器获取的受保护资源。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>受保护资源</title>
<link rel="stylesheet" th:href="@{/css/protectedResource.css}" />
</head>
<body>
<div class="protected-resource-container">
<h2>受保护资源</h2>
<p th:text="${resource}"></p>
<p>
<a href="/">返回主页</a>
</p>
</div>
</body>
</html>
现在,你已经创建了一个客户端应用。要启动应用,请运行 ClientApplication
主类。启动后,访问 http://localhost:8080
,然后点击登录。登录成功后,你将被重定向回客户端应用的主页。点击链接以访问受保护的资源。