鍵マント+springセキュリティトークンリリース年代
47531 ワード
私はkeycook APIの公式ドキュメントの方法に従ってaccesstokenを取得することを選択しました.
しかし、ほとんどのブログ記事はspringsecurityにバインドされています.
その時私は知識が足りなかったのに、どうしてそんなことをしたのですか.と思った.
すべて原因がある.
なぜなら、必要な情報をjsonパケット化するためにaccesstokenを私のように直接復号する方法です.
あまりよくないのは...まず,MSAベースの各サービスにはそれぞれのサーバがある.
サーバが異なる場合、モデルもセッションも無駄です.
だからspring-httpsessionではredisを使うことが多い.
ただし、ログアウト機能を実装している場合はredisを使用する必要があります.
ログアウト機能以外はログイン機能のみで、redisタグなしでの移動が許可されている場合は、
次に~springsecurityとkeycoatkがあり、トークン情報をprincipalオブジェクトに格納する.
KEYマントAccesstokenをjwtTokenに変換します.
すなわち、tokenのuserinfoはgetUserAttributes()として受信される.
やってみようぜ!
まず鍵マントをgatewayに接続します
しかし、ほとんどのブログ記事はspringsecurityにバインドされています.
その時私は知識が足りなかったのに、どうしてそんなことをしたのですか.と思った.
すべて原因がある.
なぜなら、必要な情報をjsonパケット化するためにaccesstokenを私のように直接復号する方法です.
あまりよくないのは...まず,MSAベースの各サービスにはそれぞれのサーバがある.
サーバが異なる場合、モデルもセッションも無駄です.
だからspring-httpsessionではredisを使うことが多い.
ただし、ログアウト機能を実装している場合はredisを使用する必要があります.
ログアウト機能以外はログイン機能のみで、redisタグなしでの移動が許可されている場合は、
次に~springsecurityとkeycoatkがあり、トークン情報をprincipalオブジェクトに格納する.
KEYマントAccesstokenをjwtTokenに変換します.
すなわち、tokenのuserinfoはgetUserAttributes()として受信される.
やってみようぜ!
まず鍵マントをgatewayに接続します
SecurityConfig.java
package com.dream.gatewayservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().pathMatchers("/menu/**").permitAll()
.and().authorizeExchange().anyExchange().authenticated()
.and().oauth2Login() // to redirect to oauth2 login page.
.and().csrf().disable();
return http.build();
}
}
まず安心感を設定します.
application.yml
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: http://너님의 키클락사용할 pc url:8080/auth/realms/MSA
registration:
keycloak:
# provider: keycloak
client-id: spring-gateway-client
client-secret: 클라이언트 비번
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/keycloak'
keycloak:
realm: MSA
# public-client: false
resource: spring-gateway-client
auth-server-url: http://너님의 키클락사용할 pc url:8080/auth
このような
鍵のマントの情報を知るから~!
build.gradle
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.dream'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2021.0.1")
set('keyCloakVersion', "16.1.1") //추가
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' //추가
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
//implementation 'org.keycloak:keycloak-spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keyCloakVersion}" //추가
}
}
tasks.named('test') {
useJUnitPlatform()
}
//追加と書かれたすべての部分を追加します.それは鍵マントが縛られていることです.
鍵マントをMSに安全に接続してトークンを取得する
ManageConfig.java
package com.dream.manage.config;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class ManageConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().oauth2ResourceServer()
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
.and().anonymous().disable();
}
private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter() {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new RealmRoleConverter());
return jwtConverter;
}
public class RealmRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
@Override
public Collection<GrantedAuthority> convert(Jwt jwt) {
final Map<String, List<String>> realmAccess = (Map<String, List<String>>) jwt.getClaims()
.get("realm_access");
return realmAccess.get("roles").stream().map(roleName -> "ROLE_" + roleName)
// prefix required by Spring
// Security for roles.
.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
}
}
まずconfigは各サービスに対して行うべきである.
application.yml
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://keycloak 돌릴 pc url:8080/auth/realms/MSA/protocol/openid-connect/certs
# client:
# provider:
# keycloak:
# issuer-uri: http://keycloak 돌릴 pc url:8080/auth/realms/MSA
# registration:
# keycloak:
## provider: keycloak
# client-id: spring-gateway-client
# client-secret: keycloak client 비번
# authorization-grant-type: authorization_code
# redirect-uri: '{baseUrl}/login/oauth2/code/keycloak'
keycloak:
realm: MSA
bearer-only: true
ssl-required: external
resource: spring-gateway-client
auth-server-url: http://keycloak 돌릴 pc url:8080/auth
credentials:
secret: keycloak client 비번
これを入れてください.
build.gradle
plugins {
id 'org.springframework.boot' version '2.6.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.dream'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2021.0.1")
set('keycloakversion', "16.1.1") //추가
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' //추가
implementation 'org.springframework.boot:spring-boot-starter-security' //추가
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.keycloak:keycloak-spring-boot-starter' //추가
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakversion}" //추가
}
}
tasks.named('test') {
useJUnitPlatform()
}
//追加するすべての部分を追加してください
次にcontrollerからtokenを抽出し、tokenに必要な情報をビューに渡します.
やってみます~
manageController.Javaの変更が必要です
package com.dream.manage.controller;
import java.security.Principal;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.dream.manage.dto.ManageDto;
import com.dream.manage.service.ManageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class ManageController {
private ManageService service;
@RolesAllowed({ "ADMIN" }) //realm 의 role에 따라 mapping
@GetMapping("/register")
public String register(Principal principal, Model model) {
JwtAuthenticationToken token = (JwtAuthenticationToken) principal; //jwt로 형변환
log.info("toString : "+token.getTokenAttributes().toString());
model.addAttribute("list", token.getTokenAttributes());
return "register";
}
@RolesAllowed({ "ADMIN" })
@PostMapping("/register")
public String register(@ModelAttribute("dto") ManageDto dto) {
log.info("컨트롤러 시작" + dto.getDescription());
service.register(dto);
log.info("컨트롤러 끝");
return "/regist_success";
}
@PostMapping("/modify")
public String modify() {
return "asd";
}
@GetMapping("/delete")
public String delete() {
return "asd";
}
}
register.htmlでは、<div th:text="${list.preferred_username}">
出して使えばいいです.
これで他のMSも可能です.
結果
まずlocalhost:8000/manage/registerにユーザーとしてアクセスします.
ポート番号8000はapiゲートウェイポート番号です.
@RolesAllowed({"ADMIN"})によって拒否されました
今回はadminアクセスを使ってみました
このようにusernameが現れているのが見えます.
Reference
この問題について(鍵マント+springセキュリティトークンリリース年代), 我々は、より多くの情報をここで見つけました
https://velog.io/@tkaqhcjstk/keycloak-spring-security-토큰-발급-연대기
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
package com.dream.gatewayservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().pathMatchers("/menu/**").permitAll()
.and().authorizeExchange().anyExchange().authenticated()
.and().oauth2Login() // to redirect to oauth2 login page.
.and().csrf().disable();
return http.build();
}
}
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: http://너님의 키클락사용할 pc url:8080/auth/realms/MSA
registration:
keycloak:
# provider: keycloak
client-id: spring-gateway-client
client-secret: 클라이언트 비번
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/keycloak'
keycloak:
realm: MSA
# public-client: false
resource: spring-gateway-client
auth-server-url: http://너님의 키클락사용할 pc url:8080/auth
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.dream'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2021.0.1")
set('keyCloakVersion', "16.1.1") //추가
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' //추가
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
//implementation 'org.keycloak:keycloak-spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keyCloakVersion}" //추가
}
}
tasks.named('test') {
useJUnitPlatform()
}
ManageConfig.java
package com.dream.manage.config;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class ManageConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().oauth2ResourceServer()
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
.and().anonymous().disable();
}
private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter() {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new RealmRoleConverter());
return jwtConverter;
}
public class RealmRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
@Override
public Collection<GrantedAuthority> convert(Jwt jwt) {
final Map<String, List<String>> realmAccess = (Map<String, List<String>>) jwt.getClaims()
.get("realm_access");
return realmAccess.get("roles").stream().map(roleName -> "ROLE_" + roleName)
// prefix required by Spring
// Security for roles.
.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
}
}
まずconfigは各サービスに対して行うべきである.application.yml
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://keycloak 돌릴 pc url:8080/auth/realms/MSA/protocol/openid-connect/certs
# client:
# provider:
# keycloak:
# issuer-uri: http://keycloak 돌릴 pc url:8080/auth/realms/MSA
# registration:
# keycloak:
## provider: keycloak
# client-id: spring-gateway-client
# client-secret: keycloak client 비번
# authorization-grant-type: authorization_code
# redirect-uri: '{baseUrl}/login/oauth2/code/keycloak'
keycloak:
realm: MSA
bearer-only: true
ssl-required: external
resource: spring-gateway-client
auth-server-url: http://keycloak 돌릴 pc url:8080/auth
credentials:
secret: keycloak client 비번
これを入れてください.build.gradle
plugins {
id 'org.springframework.boot' version '2.6.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.dream'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2021.0.1")
set('keycloakversion', "16.1.1") //추가
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' //추가
implementation 'org.springframework.boot:spring-boot-starter-security' //추가
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.keycloak:keycloak-spring-boot-starter' //추가
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakversion}" //추가
}
}
tasks.named('test') {
useJUnitPlatform()
}
//追加するすべての部分を追加してください次にcontrollerからtokenを抽出し、tokenに必要な情報をビューに渡します.
やってみます~
manageController.Javaの変更が必要です
package com.dream.manage.controller;
import java.security.Principal;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.dream.manage.dto.ManageDto;
import com.dream.manage.service.ManageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@RequiredArgsConstructor
@Slf4j
public class ManageController {
private ManageService service;
@RolesAllowed({ "ADMIN" }) //realm 의 role에 따라 mapping
@GetMapping("/register")
public String register(Principal principal, Model model) {
JwtAuthenticationToken token = (JwtAuthenticationToken) principal; //jwt로 형변환
log.info("toString : "+token.getTokenAttributes().toString());
model.addAttribute("list", token.getTokenAttributes());
return "register";
}
@RolesAllowed({ "ADMIN" })
@PostMapping("/register")
public String register(@ModelAttribute("dto") ManageDto dto) {
log.info("컨트롤러 시작" + dto.getDescription());
service.register(dto);
log.info("컨트롤러 끝");
return "/regist_success";
}
@PostMapping("/modify")
public String modify() {
return "asd";
}
@GetMapping("/delete")
public String delete() {
return "asd";
}
}
register.htmlでは、<div th:text="${list.preferred_username}">
出して使えばいいです.これで他のMSも可能です.
結果
まずlocalhost:8000/manage/registerにユーザーとしてアクセスします.
ポート番号8000はapiゲートウェイポート番号です.
@RolesAllowed({"ADMIN"})によって拒否されました
今回はadminアクセスを使ってみました
このようにusernameが現れているのが見えます.
Reference
この問題について(鍵マント+springセキュリティトークンリリース年代), 我々は、より多くの情報をここで見つけました
https://velog.io/@tkaqhcjstk/keycloak-spring-security-토큰-발급-연대기
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
Reference
この問題について(鍵マント+springセキュリティトークンリリース年代), 我々は、より多くの情報をここで見つけました https://velog.io/@tkaqhcjstk/keycloak-spring-security-토큰-발급-연대기テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol