OAuth-Unterstuetzung fuer jdk.httpserver
ulrich
2021-07-05 8c1928946cb3b4f2d9ead70c7362ce1dbe045fa4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
  http-oauth - OAuth Extensions to jdk.httpserver
  Copyright (C) 2021  Ulrich Hilger
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.
 
  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package de.uhilger.httpserver.oauth;
 
import com.google.gson.Gson;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import de.uhilger.httpserver.auth.realm.User;
import de.uhilger.httpserver.base.HttpHelper;
import de.uhilger.httpserver.base.HttpResponder;
import java.io.IOException;
import java.util.logging.Logger;
 
/**
 * Ein Login Handler, der zur Authentifizierung ein Objekt der Klasse 
 * BearerAuthenticator im HttpContext benoetigt.
 * 
 * Der Authenticator wird mit der Methode 
 * context.getAttributes().get(ATTR_AUTHENTICATOR); 
 * aus dem HttpContext entnommen, d.h., der Authenticator muss zuvor dort 
 * eingetragen werden. Das kann wie folgt vonstatten gehen:
 * 
 * HttpContext context = server.createContext("/myapp/secure/service", new SomeServiceHandler());
 * BearerApiAuthenticator auth = new BearerAuthenticator();
 * context.setAuthenticator(auth);
 * 
 * ...und danach...
 * 
 * context = server.createContext("/myapp/login", new BearerLoginHandler());
 * context.getAttributes().put(LoginHandler.ATTR_AUTHENTICATOR, auth);
 * 
 * @author Ulrich Hilger
 * @version 1, 08.06.2021
 */
public class BearerLoginHandler implements HttpHandler {
  
  private static final Logger logger = Logger.getLogger(BearerLoginHandler.class.getName());
  
  public static final String ATTR_AUTHENTICATOR = "authenticator";
  
  public static final String CACHE_CONTROL = "Cache-Control";
  public static final String NO_STORE = "no-store";
  public static final String PRAGMA = "Pragma";
  public static final String NO_CACHE = "no-cache";
  public static final String BEARER_CONTENT_TYPE = "application/json;charset=UTF-8";
  
  /*
    gemaess RFC 6750 lautet die Antwort auf eine erfolgreiche Anmeldung
    wie folgt:
  
     HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache
 
     {
       "access_token":"mF_9.B5f-4.1JqM",
       "token_type":"Bearer",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
     }
  */
  /**
   * Login-Anfragen ausfuehren
   * 
   * @param exchange das Objekt mit Informationen zu HTTP-Anfrage und -Antwort
   * @throws IOException 
   */
  @Override
  public void handle(HttpExchange exchange) throws IOException {
    HttpContext context = exchange.getHttpContext();
    Object o = context.getAttributes().get(ATTR_AUTHENTICATOR);
    if (o instanceof BearerAuthenticator) {
      BearerAuthenticator auth = (BearerAuthenticator) o;
      User user = getUser(exchange);
      LoginResponse response = auth.login(exchange, user.getName(), user.getPassword());
      handleLoginResponse(exchange, response);
    } else {
      HttpResponder r = new HttpResponder();
      r.antwortSenden(exchange, 500, "No suitable authenticator.");
    }
  }
  
  /**
   * Die Antwort des Authenticators auf eine Login-Anfrage verarbeiten
   * @param exchange das Objekt mit Informationen zu HTTP-Anfrage und -Antwort
   * @param response die Antwort des Autehnticators
   * @throws IOException 
   */
  protected void handleLoginResponse(HttpExchange exchange, LoginResponse response) throws IOException {
    if(response != null) {
      setLoginHeader(exchange);
      HttpResponder r = new HttpResponder();
      r.antwortSenden(exchange, 200, response.toJson());
    } else {
      HttpResponder r = new HttpResponder();
      r.antwortSenden(exchange, 406, "Login failed.");
    }
  }
  
  private void setLoginHeader(HttpExchange exchange) {
    Headers headers = exchange.getResponseHeaders();
    headers.add(HttpHelper.CONTENT_TYPE, BEARER_CONTENT_TYPE);
    headers.add(CACHE_CONTROL, NO_STORE);
    headers.add(PRAGMA, NO_CACHE);
  } 
  
  private User getUser(HttpExchange exchange) throws IOException {
    /*
    Wenn ein JSON-Inhalt im Body uebermittelt wird, steht
    dort evtl. etwas wie
    {"name": "fred", "password": "secret"}
    das kann wie folgt gelesen werden
     */
    String body = new HttpHelper().bodyLesen(exchange);
    Gson gson = new Gson();
    User user = gson.fromJson(body, User.class);
    return user;
  }
  
  
}