[JAVA] Ich habe versucht, einen OAuth-Client mit Spring Boot / Security (LINE-Anmeldung) zu implementieren.

1. Autorisierungsservereinstellung (LINE)

LINE Developers: https://developers.line.biz/ja

・ Kanaltyp: LINE Login ・ App-Typen: Web-App -Callback-URL: http : // localhost: 8080 / redirect (Ziel-URL nach Authentifizierung umleiten)

2. Client-Implementierung (Spring Boot / Sicherheit)

2-1. Hinzufügen abhängiger Bibliotheken

pom.xml


    <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-thymeleaf</artifactId>
    </dependency>
        <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    </dependency>
2-2. Einstellung von application.yml

Wechseln Sie den Benutzer basierend auf den folgenden Inhalten zum Bildschirm LINE-Authentifizierung und rufen Sie die LINE-API auf, um Benutzerinformationen abzurufen

security.oauth2.client.clientId  - Channnel ID security.oauth2.client.clientSecret  - Channnel Secret security.oauth2.client.accessTokenUri

application.yml


spring:
  main:
    allow-bean-definition-overriding: true
security:
  oauth2:
    client:
      clientId: [CLIENT_ID]
      clientSecret: [CLIENT_SECRET]
      accessTokenUri: https://api.line.me/oauth2/v2.1/token
      userAuthorizationUri: https://access.line.me/oauth2/v2.1/authorize
      use-current-uri: false
      pre-established-redirect-uri: https://localhost:8443/redirect
      authorized-grant-types:
      - authorization_code
      - refresh_token
      clientAuthenticationScheme: form
      scope:
        - openid
        - email
        - profile
    resource:
      userInfoUri: https://api.line.me/oauth2/v2.1/userinfo
      preferTokenInfo: true
server:
  port: 8443
  ssl:
    key-store: [.p12 Dateipfad]
    key-store-password: [Passwort]
    keyStoreType: PKCS12
    keyAlias: MyAlias
logging:
  level:
     org.springframework.web: DEBUG    
2-3. Abrufen von Variableninformationen aus application.yml

ConfigComponent.java


import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:application.yml")
@ConfigurationProperties(prefix = "security.oauth2.client")
public class ConfigComponent {

    private String accessTokenUri;

    private String clientId;

    private String clientSecret;
    
    @Value("${security.oauth2.client.pre-established-redirect-uri}")
    private String redirecturi;
    
    @Value("${security.oauth2.resource.userInfoUri}")
    private String userInfoUri;

    public String getAccessTokenUri() {
        return this.accessTokenUri;
    }
    
    public void setAccessTokenUri(String accessTokenUri) {
        this.accessTokenUri = accessTokenUri;
    }

    public String getClientId() {
        return this.clientId;
    }
    
    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getRedirecturi() {
        return this.redirecturi;
    }

    public void setRedirecturi(String redirecturi) {
        this.redirecturi = redirecturi;
    }
    
    public String getClientSecret() {
        return this.clientSecret;
    }
    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }
    
    public String getUserInfoUri() {
        return this.userInfoUri;
    }
    public void setUserInfoUri(String userInfoUri) {
        this.userInfoUri = userInfoUri;
    }
}
2-4. @Bean Registrierung

ConfigBean.java


package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

    @Bean
    public ConfigComponent configComponent() {
        return new ConfigComponent();
    }
    
    @Bean
    public LoginRestClient loginRestClient() {
        return new LoginRestClient();
    }
}
2-5. Führen Sie die OAuth-Authentifizierung aus, wenn der Benutzer auf den angegebenen Pfad zugreift (/ login).

SecurityConfig.java


import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;

@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                            "/images/**",
                            "/css/**",
                            "/javascript/**",
                            "/webjars/**",
                            "/favicon.ico");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	http
	    	.authorizeRequests()
		        .antMatchers("/redirect/**")
		        	.permitAll()
		        .and()
	        .authorizeRequests()
		        .antMatchers("/login/**")
		        .authenticated();
    }   
}
2-5. Definition von @Controller

--Nach Abschluss der Authentifizierung erhalten Sie den Autorisierungscode und führen die Anforderung zur Ausgabe von Zugriffstoken / zur Erfassung von Benutzerinformationen aus. -Injizieren Sie die LoginRestClient-Klasse mit @Autowired

LoginController.java


import static java.util.Objects.requireNonNull;

import java.io.IOException;
import java.util.stream.IntStream;

import com.example.demo.LoginRestClient;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {
	
	@Autowired
	LoginRestClient loginRestClient;
	
	@RequestMapping(value = "/redirect", method = RequestMethod.GET)
	public String getToken(@RequestParam("code") String code,
			HttpServletRequest req, HttpServletResponse res) throws IOException{
	  LineToken responseToken = loginRestClient.getToken(code);
	  String accessTK = responseToken.getAccessTK();
	  String refreshTK = responseToken.getRefreshTK();
	  String idTK = responseToken.getIdTK();
	  
	  Cookie accessTKCookie = new Cookie("access_token", accessTK);
	  accessTKCookie.setSecure(true);
	  Cookie refreshTKCookie = new Cookie("refresh_token", refreshTK);
	  refreshTKCookie.setSecure(true);
	  Cookie idTKCookie = new Cookie("id_token", idTK);
	  idTKCookie.setSecure(true);
	  
	  res.addCookie(accessTKCookie);
	  res.addCookie(refreshTKCookie);
	  res.addCookie(idTKCookie);
	  
	  return "redirect:/";
    }

    @RequestMapping(value = "/userinfo", method = RequestMethod.GET)
    public ModelAndView getSubInfo(ModelAndView mv, HttpServletRequest req) throws IOException {
    	Cookie cookies[] = req.getCookies();
    	for(Cookie c: cookies) {
    		if(c.getName().equals("access_token")) {
    			System.err.println(c.getValue());
    			JSONObject userInfo =
    					new JSONObject(loginRestClient.getUserInfo(c.getValue()).getBody());

    		  	String sub = userInfo.getString("sub");
    		  	String name = userInfo.getString("name");
    		  	String picture = userInfo.getString("picture");

    			mv.addObject("sub", sub);
    			mv.addObject("name", name);
    			mv.addObject("picture", picture);
    			mv.setViewName("userinfo");
    		}
    	}
    	return mv;
    	
    	}
    
	@RequestMapping(value = "/", method = RequestMethod.GET)
 	public ModelAndView getTopPage(ModelAndView mv, HttpServletRequest req) {
    	Cookie cookies[] = req.getCookies();
    	for(Cookie c: cookies) {
    		if(c.getName().equals("access_token")) {
    			mv.addObject("access_token", c.getValue());
    		} else if (c.getName().equals("refresh_token")) {
    			mv.addObject("refresh_token", c.getValue());
    		} else if (c.getName().equals("id_token")) {
    			mv.addObject("id_token", c.getValue());
    		}
    	}

    	mv.setViewName("index");
    	
    	return mv;
     }
}
2-6. Anforderungsausführungsklasse zum Ausstellen des Zugriffstokens / Erfassen von Benutzerinformationen

LoginRestClient.java


import java.io.IOException;
import java.util.Map;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

@Component
public class LoginRestClient {
	
	final static RestTemplate rs = new RestTemplate();

	@Autowired
	private ConfigComponent cc;
	
    public LineToken getToken(String code) throws IOException {
    	code = Objects.requireNonNull(code);
    	HttpHeaders headers = new HttpHeaders();
    	headers.setBasicAuth(cc.getClientId(), cc.getClientSecret());
    	headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    	
    	MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
    	params.add("code", code);
    	params.add("redirect_uri", cc.getRedirecturi());
    	params.add("grant_type", "authorization_code");
    	
    	HttpEntity<MultiValueMap<String, Object>> requestEntity 
    		= new HttpEntity<>(params, headers);
    	
        LineToken response = rs
                .postForObject(cc.getAccessTokenUri(), requestEntity, LineToken.class);
    	
    	return response;
    }
    
    public ResponseEntity<String> getUserInfo(String accessToken) throws IOException {
    	accessToken = Objects.requireNonNull(accessToken);
    	HttpHeaders headers = new HttpHeaders();
    	headers.setBearerAuth(accessToken);
    	
    	HttpEntity<MultiValueMap<String, Object>> requestEntity 
			= new HttpEntity<>(headers);
    	
    	 ResponseEntity<String> response =
    			 rs.exchange(cc.getUserInfoUri(), HttpMethod.GET, requestEntity, String.class);
    	
    	System.err.println(response);
    	
    	return response;
    }

}
2-7. POJO-Konvertierung von Zugriffstoken / Aktualisierungstoken / ID-Token

LineToken.java


import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import static java.util.Objects.requireNonNull;

public final class LineToken {
    public final String accessToken;
    public final String idToken;
    public final String refreshToken;

    @JsonCreator
    LineToken(
        @JsonProperty("access_token") String accessToken,
        @JsonProperty("id_token") String idToken,
        @JsonProperty("refresh_token") String refreshToken
    ) {
        this.accessToken = requireNonNull(accessToken, "accessToken");
        this.idToken = requireNonNull(idToken, "idToken");
        this.refreshToken = requireNonNull(refreshToken, "refreshToken");
    }
    
    public String getAccessTK() {return this.accessToken;}
    public String getIdTK() {return this.idToken;}
    public String getrefreshTK() {return this.refreshToken;}
    
}

3. Überprüfung

3-1. Zugriff auf https : // localhost: 8443 / login
3-2. Weiterleiten zum LINE-Anmeldebildschirm
キャプチャ.PNG
3-3. Anmeldeausführung
3-4. Decodiere id_token

Da das ID-Token im JWT-Format zurückgegeben wird, dekodiert Base64 den Header- / Payload-Teil und verweist auf den Inhalt.

Header: {"typ":"JWT","alg":"HS256"}

Nutzlast: {"iss":"https://access.line.me","sub":"**********","aud":"1653797412","exp":1579957272,"iat":1579953672,"amr":["linesso"],"name":"Yu","picture":"https://profile.line-scdn.net/0ho4KGrFFFMBt2PhvCq9pPTEp7PnYBEDZTDg98LVpsanhTXn4aTFx5Lwdpbn9SWnEYHwgtfFs3Znhd"}

Anspruchsname Inhalt beanspruchen
iss Token-Aussteller(ISSuer)
sub Ziel von Token(SUBubect)
aud Token-Empfänger(AUDience)
exp Ablaufdatum des Tokens(EXPiration time)
iat Zeitstempel für die Ausgabe von Token(Issued-AT)

Recommended Posts

Ich habe versucht, einen OAuth-Client mit Spring Boot / Security (LINE-Anmeldung) zu implementieren.
Teil 1: Versuchen Sie, die von Spring Security 5 unterstützte OAuth 2.0-Anmeldung mit Spring Boot zu verwenden
SNS-Login mit Spring Boot
Anmeldefunktion mit Spring Security
Implementierte Authentifizierungsfunktion mit Spring Security ②
Implementierte Authentifizierungsfunktion mit Spring Security ③
Erstellen Sie eine App mit Spring Boot 2
Implementierte Authentifizierungsfunktion mit Spring Security ①
Erstellen Sie eine App mit Spring Boot
Ich habe GraphQL mit Spring Boot ausprobiert
[Java] LINE-Integration mit Spring Boot
Ich habe Flyway mit Spring Boot ausprobiert
Ich habe Lazy Initialization mit Spring Boot 2.2.0 ausprobiert
Erreichen Sie die BASIC-Authentifizierung mit Spring Boot + Spring Security
[LINE BOT] Ich habe einen Ramen BOT mit Java (Maven) + Heroku + Spring Boot (1) gemacht.
Hash-Passwörter mit Spring Boot + Spring Security (mit Salt, mit Stretching)
Versuchen Sie die LDAP-Authentifizierung mit Spring Security (Spring Boot) + OpenLDAP
Versuchen Sie, die Anmeldefunktion mit Spring Boot zu implementieren
Ich wollte Spring Boot in einem Multiprojekt gradle
[Einführung in Spring Boot] Authentifizierungsfunktion mit Spring Security
Erstellen Sie mit Spring Boot 2.0 einen Spring Cloud Config Server mit Sicherheit
Ich habe mit Spring Framework eine API-Domain erstellt. Teil 2
Verwendungshinweis zu Spring Security: Zusammenarbeit mit Spring MVC und Boot
Spring Boot mit Spring Security Filter-Einstellungen und Suchtpunkten
Implementieren Sie eine einfache Rest-API mit Spring Security mit Spring Boot 2.0
SSO mit GitHub OAuth in der Spring Boot 1.5.x-Umgebung
Erstellen Sie mit Spring Security 2.1 eine einfache Demo-Site mit Spring Security
Ich habe mit Spring Framework eine API-Domain erstellt. Teil 1
Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben
Mit Spring Boot herunterladen
Hash beim Spring-Boot das Passwort und verwenden Sie die Mitgliederregistrierung und die Spring-Sicherheit, um die Anmeldefunktion zu implementieren.
Ich habe das Spring Boot-Einführungshandbuch [Zugriff auf Daten mit JPA] ausprobiert.
Ich habe versucht, mit Swagger mit Spring Boot zu beginnen
Implementieren Sie eine einfache Rest-API mit Spring Security & JWT mit Spring Boot 2.0
Melden Sie sich mit HttpServletRequest # in Spring Security in der Servlet 3.x-Umgebung an
Generieren Sie mit Spring Boot einen Barcode
Hallo Welt mit Spring Boot
Implementieren Sie GraphQL mit Spring Boot
Beginnen Sie mit Spring Boot
Hallo Welt mit Spring Boot!
Führen Sie LIFF mit Spring Boot aus
Datei-Upload mit Spring Boot
Spring Boot beginnt mit dem Kopieren
Spring Boot beginnend mit Docker
Hallo Welt mit Spring Boot
Setzen Sie Cookies mit Spring Boot
Verwenden Sie Spring JDBC mit Spring Boot
Modul mit Spring Boot hinzufügen
Erste Schritte mit Spring Boot
[Spring Boot] Senden Sie eine E-Mail
Versuchen Sie es mit Spring Boot Security
Erstellen Sie mit Spring Boot einen Mikrodienst
Mail mit Spring Boot verschicken
API-Integration von Java mit Jersey Client
Java zum Spielen mit Function
GraphQL Client beginnend mit Ruby
Ich habe versucht, einen OAuth-Client mit Spring Boot / Security (LINE-Anmeldung) zu implementieren.
[Java EE] Implementieren Sie den Client mit WebSocket
Doppelte Übermittlung von Maßnahmen mit Play Framework
Oauth2-Authentifizierung mit Spring Cloud Gateway
Tweak Markdown mit Java Flexmark-Java
Bildverarbeitung: Spielen wir mit dem Bild
Beim Testen der Formularauthentifizierung mit Spring Security tritt ein 404-Fehler auf
02. Ich habe eine API erstellt, um eine Verbindung von Spring Boot zu MySQL (My Batis) herzustellen.
Teil 4: Passen Sie das Verhalten der von Spring Security 5 unterstützten OAuth 2.0-Anmeldung an
Ich habe ein einfaches Suchformular mit Spring Boot + GitHub Search API erstellt.
Datenbankverknüpfung mit doma2 (Spring Boot)