Spring Boot/Angular整合Keycloak实现单点登录功能

下面是Spring Boot/Angular整合Keycloak实现单点登录功能的完整攻略。

一、准备工作

1.安装并配置Java环境和Maven环境。

2.安装Keycloak,并创建相关的Realm和Client。

3.创建一个Angular项目,引入相关依赖。

二、配置Keycloak

1.打开Keycloak控制台,在Realm Setting中设置Valid Redirect URIs为http://localhost:4200/*

2.创建Client,设置Access Type为confidential,Valid Redirect URIs为http://localhost:4200/*

3.在Client Scope里添加一个Role,如:user,并分配给一个用户。

三、配置Spring Boot后端

1.引入相关依赖:

<dependencies>
    <!-- Spring Security Web -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.2.2.RELEASE</version>
    </dependency>
    <!-- Spring Security OAuth2 Client -->
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.3.3.RELEASE</version>
    </dependency>
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
        <version>2.3.3.RELEASE</version>
    </dependency>
    <!-- Keycloak Adapter Spring Security -->
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-security-adapter</artifactId>
        <version>11.0.1</version>
    </dependency>
</dependencies>

2.添加配置类

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/api/**").hasRole("user")
                .anyRequest().permitAll();
        http.csrf().disable();
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        // 持久化session
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public KeycloakConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

3.在配置文件中进行相关配置

server:
  port: 8080

keycloak:
  auth-server-url: http://localhost:8081/auth
  realm: demo
  resource: demo-backend

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: demo-backend
            client-secret: 3094e29b-7450-4a9f-bfa5-1501e56fc8d7
            access-token-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/token
            user-authorization-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/auth
            issuer-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}
            scope: openid
        provider:
          keycloak:
            issuer-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}

四、配置Angular前端

1.安装相关依赖

npm install --save angular-oauth2-oidc
npm install --save @auth0/angular-jwt

2.创建AuthService,实现登录、注销和Token验证的逻辑

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly AUTH_SERVER = 'http://localhost:8081/auth/realms/demo';
  private readonly LOGIN_URL = `${this.AUTH_SERVER}/protocol/openid-connect/auth`;
  private readonly CLIENT_ID = 'demo-frontend';
  private readonly RESPONSE_TYPE = 'code';
  private readonly SCOPE = 'openid profile email';
  private readonly REDIRECT_URI = 'http://localhost:4200/callback';
  private readonly JWK_URI = `${this.AUTH_SERVER}/protocol/openid-connect/certs`;
  private readonly TOKEN_URL = `${this.AUTH_SERVER}/protocol/openid-connect/token`;
  private readonly LOGOUT_URL = `${this.AUTH_SERVER}/protocol/openid-connect/logout`;

  private tokenSubject = new BehaviorSubject<Token>(null);
  public token$ = this.tokenSubject.asObservable();

  constructor(private readonly http: HttpClient,
              private readonly oauthService: OAuthService,
              private readonly jwtService: JwtHelperService) {
    oauthService.configure({
      issuer: this.AUTH_SERVER,
      clientId: this.CLIENT_ID,
      redirectUri: this.REDIRECT_URI,
      scope: this.SCOPE,
      responseType: this.RESPONSE_TYPE,
      tokenEndpoint: this.TOKEN_URL,
      userinfoEndpoint: `${this.AUTH_SERVER}/protocol/openid-connect/userinfo`,
      jwksEndpoint: this.JWK_URI
    });

    oauthService.loadDiscoveryDocumentAndTryLogin();

    oauthService.events.subscribe(
      e => console.log(e)
    );
  }

  public isAuthenticated(): boolean {
    const jwt = this.oauthService.getAccessToken();
    return !this.jwtService.isTokenExpired(jwt);
  }

  public async login(): Promise<void> {
    this.oauthService.initLoginFlow();
    await this.oauthService.tryLogin({});
  }

  public async logout(): Promise<void> {
    await this.http.get(this.LOGOUT_URL).toPromise();
    this.oauthService.logOut();
  }
}

3.在AppComponent中使用AuthService

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="authService.token$ | async as token; else loginTpl">
      <p>用户{{ token.userName }}已登录</p>
      <button (click)="logout()">注销</button>
      <router-outlet></router-outlet>
    </div>
    <ng-template #loginTpl>
      <button (click)="login()">登录</button>
    </ng-template>`,
  styles: []
})
export class AppComponent {
  constructor(public readonly authService: AuthService) {
  }

  public login(): void {
    this.authService.login();
  }

  public logout(): void {
    this.authService.logout();
  }
}

五、示例

这里提供两个示例:

  1. 等级管理系统

场景:一个等级管理系统,管理员可以添加、删除用户等级,只有等级为1的用户才能访问系统。

前端:等级管理页面,管理员登录后,在页面中展示当前系统所有的用户等级,提供添加、删除等操作的按钮。

后端:提供等级相关的api,get和post请求需登录后才能访问。

后端接口代码:

@RestController
public class RankController {
    @GetMapping("/api/ranks")
    public List<Integer> getRanks(HttpServletRequest request) {
        return Arrays.asList(1, 2, 3);
    }

    @PostMapping("/api/ranks")
    public void addRank(HttpServletRequest request, @RequestParam("rank") int rank) {
        // 添加等级的逻辑
    }

    @DeleteMapping("/api/ranks")
    public void deleteRank(HttpServletRequest request, @RequestParam("rank") int rank) {
        // 删除等级的逻辑
    }
}

在SecurityConfig中配置get和post请求需要登录:

@Override
protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http.authorizeRequests()
            .antMatchers("/api/**").hasRole("user")
            .anyRequest().permitAll();
    http.csrf().disable();
}

前端接口代码:

@Injectable({
  providedIn: 'root'
})
export class RanksService {
  private readonly URL = 'http://localhost:8080/api/ranks';

  constructor(private readonly http: HttpClient) {
  }

  public getRanks(): Observable<number[]> {
    return this.http.get<number[]>(this.URL);
  }

  public addRank(rank: number): void {
    this.http.post(this.URL, {rank}).subscribe();
  }

  public deleteRank(rank: number): void {
    const options = {params: new HttpParams().set('rank', rank)};
    this.http.delete(this.URL, options).subscribe();
  }
}
  1. 论坛系统

场景:一个论坛系统,只有登录用户才能发帖或回复,用户可以在header中看到自己的用户名。

前端:提供发帖、回复、帖子列表的页面,header中展示当前用户的用户名。

后端:提供帖子相关的api,get和post请求需登录后才能访问。

后端接口代码:

@RestController
public class PostController {
    @GetMapping("/api/posts")
    public List<String> getPosts(HttpServletRequest request) {
        return Arrays.asList("Post 1", "Post 2", "Post 3");
    }

    @PostMapping("/api/posts")
    public void addPost(HttpServletRequest request, @RequestBody Map<String, String> post) {
        // 添加帖子的逻辑
    }
}

在SecurityConfig中配置get和post请求需要登录:

@Override
protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http.authorizeRequests()
            .antMatchers("/api/**").hasRole("user")
            .anyRequest().permitAll();
    http.csrf().disable();
}

前端接口代码:

@Injectable({
  providedIn: 'root'
})
export class PostsService {
  private readonly URL = 'http://localhost:8080/api/posts';

  constructor(private readonly http: HttpClient) {
  }

  public getPosts(): Observable<string[]> {
    return this.http.get<string[]>(this.URL);
  }

  public addPost(post: string): void {
    this.http.post(this.URL, {post}).subscribe();
  }
}

六、总结

以上就是Spring Boot/Angular整合Keycloak实现单点登录功能的完整攻略,整合过程中的关键步骤和示例都在上述内容中进行了详细的说明。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot/Angular整合Keycloak实现单点登录功能 - Python技术站

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

相关文章

  • Java8中的LocalDateTime你会使用了吗

    当我们需要对日期和时间进行操作时,通常使用Java的Date或Calendar对象。但是Java 8 引入了新的时间API,其中包括LocalDateTime类,可以更方便地处理日期和时间。 LocalDateTime的基本用法 LocalDateTime类是Java 8中的一个重要类,它表示日期和时间,具有年、月、日、小时、分钟、秒和毫秒等属性。与Date…

    Java 2023年5月26日
    00
  • 带你深入概括Java!六、方法和方法重载!(推荐)

    带你深入概括Java!六、方法和方法重载!(推荐) 方法的定义 Java中的方法(Method)是一段可以被重复使用的代码块,它封装了特定的功能,一般用来解决一类问题。在Java中,方法通常包括方法头和方法体两个部分,语法如下: 修饰符 返回值类型 方法名(参数列表) { // 方法体 } 其中,修饰符是可选的,如果没有修饰符,则默认为public;返回值类…

    Java 2023年5月26日
    00
  • 浅析SpringBoot自动化配置原理实现

    首先来介绍一下“浅析SpringBoot自动化配置原理实现”的完整攻略。 什么是SpringBoot自动化配置 SpringBoot是现在非常流行的Java Web开发框架,其最大的特点是其对于开发者的友好性,使开发者可以非常快地构建出一个Web应用,其中最为重要的就是其自动化配置。 自动化配置是SpringBoot的核心功能之一,它可以帮助开发者自动加载常…

    Java 2023年5月15日
    00
  • 使用Ajax实现简单的带百分比进度条实例

    使用Ajax实现简单的带百分比进度条实例 在Web开发中,经常会遇到需要上传大文件或发送复杂请求的情况,此时通常会借助Ajax实现异步上传或异步请求,提高用户体验。而在这个过程中,我们通常会通过进度条来展示任务的进度情况。在本篇文章中,我们将介绍如何使用Ajax实现简单的带百分比进度条实例。 实现步骤 以下是实现带百分比进度条的基本步骤: 创建一个进度条元素…

    Java 2023年6月15日
    00
  • java算法题解Leetcode763划分字母区间示例

    下面是“java算法题解Leetcode763划分字母区间示例”的完整攻略。 题目描述 给定一个仅包含小写字母的字符串 S,将字符串 S 划分为尽可能多的区间,使得每个字母最多出现在一个区间中,求区间的个数。 解题思路 首先,我们可以使用hashmap记录每个字母最后出现的位置,然后使用两个指针,分别记录当前合法区间的左右端点。 接着,我们遍历字符串S,记录…

    Java 2023年5月19日
    00
  • Java实习打卡8道面试题

    下面是Java实习打卡8道面试题的完整攻略。 1. 如何取数组中的随机数? 可以使用Java内置的Math.random()方法来取得一个0到1之间的随机数,然后根据数组长度与所需随机数的范围进行计算即可。 int[] nums = {1, 2, 3, 4, 5}; int range = 3; // 从数组中取3个随机数 int[] randomNums …

    Java 2023年5月26日
    00
  • 详解Spring boot操作文件的多种方式

    详解Spring Boot操作文件的多种方式 在Spring Boot应用程序中,操作文件是一个非常常见的需求。本文将详细介绍Spring Boot操作文件的多种方式,包括使用Java IO、Apache Commons IO、Spring Framework和Spring Boot提供的API。 使用Java IO操作文件 Java IO是Java标准库中…

    Java 2023年5月15日
    00
  • 详解批处理框架之Spring Batch

    详解批处理框架之Spring Batch 什么是Spring Batch Spring Batch是一个开源的批处理框架,它提供了大量的API,用于处理复杂的批处理任务。Spring Batch可以让程序员集中精力编写业务逻辑,而不必考虑如何处理批处理的细节。Spring Batch 支持事务、并发处理、监控、重启、跳过、跟踪、记录、日志等特性,是一个强大的…

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