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