OAuth-Unterstuetzung fuer jdk.httpserver
ulrich
2021-06-08 7ecde3f6cd2c516dfa5ae8a3380fa92859e72d21
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;
26 import de.uhilger.httpserver.base.handler.HttpHelper;
27 import de.uhilger.httpserver.base.handler.HttpResponder;
28 import java.io.IOException;
29 import java.util.logging.Level;
30 import java.util.logging.Logger;
31
32 /**
33  * Ein Login Handler, der zur Authentifizierung ein Objekt der Klasse 
34  * BearerAuthenticator im HttpContext benoetigt.
35  * 
36  * Der Authenticator wird mit der Methode 
37  * context.getAttributes().get(ATTR_AUTHENTICATOR); 
38  * aus dem HttpContext entnommen, d.h., der Authenticator muss zuvor dort 
39  * eingetragen werden. Das kann wie folgt vonstatten gehen:
40  * 
41  * HttpContext context = server.createContext("/myapp/secure/service", new SomeServiceHandler());
42  * BearerApiAuthenticator auth = new BearerAuthenticator();
43  * context.setAuthenticator(auth);
44  * 
45  * ...und danach...
46  * 
47  * context = server.createContext("/myapp/login", new BearerLoginHandler());
48  * context.getAttributes().put(LoginHandler.ATTR_AUTHENTICATOR, auth);
49  * 
50  * @author Ulrich Hilger
51  * @version 1, 08.06.2021
52  */
53 public class BearerLoginHandler implements HttpHandler {
54   
55   private static final Logger logger = Logger.getLogger(BearerLoginHandler.class.getName());
56   
57   public static final String ATTR_AUTHENTICATOR = "authenticator";
58   
59   public static final String CACHE_CONTROL = "Cache-Control";
60   public static final String NO_STORE = "no-store";
61   public static final String PRAGMA = "Pragma";
62   public static final String NO_CACHE = "no-cache";
63   public static final String BEARER_CONTENT_TYPE = "application/json;charset=UTF-8";
64   
65   /*
66     gemaess RFC 6750 lautet die Antwort auf eine erfolgreiche Anmeldung
67     wie folgt:
68   
69      HTTP/1.1 200 OK
70      Content-Type: application/json;charset=UTF-8
71      Cache-Control: no-store
72      Pragma: no-cache
73
74      {
75        "access_token":"mF_9.B5f-4.1JqM",
76        "token_type":"Bearer",
77        "expires_in":3600,
78        "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
79      }
80   */
81   @Override
82   public void handle(HttpExchange exchange) throws IOException {
83     HttpContext context = exchange.getHttpContext();
84     Object o = context.getAttributes().get(ATTR_AUTHENTICATOR);
85     if (o instanceof BearerAuthenticator) {
86       BearerAuthenticator auth = (BearerAuthenticator) o;
87       User user = getUser(exchange);
88       LoginResponse response = auth.login(user.getName(), user.getPassword());
89       handleLoginResponse(exchange, response);
90       /*
91       if(response != null) {
92         // hier erfolg melden
93         // 200 OK
94         setLoginHeader(exchange);
95         HttpResponder r = new HttpResponder();
96         r.antwortSenden(exchange, 200, response.toJson());
97       } else {
98         HttpResponder r = new HttpResponder();
99         r.antwortSenden(exchange, 406, "Login failed.");
100       }
101       */
102     } else {
103       HttpResponder r = new HttpResponder();
104       r.antwortSenden(exchange, 500, "No suitable authenticator.");
105     }
106   }
107   
108   protected void handleLoginResponse(HttpExchange exchange, LoginResponse response) throws IOException {
109     if(response != null) {
110       // hier erfolg melden
111       // 200 OK
112       setLoginHeader(exchange);
113       HttpResponder r = new HttpResponder();
114       r.antwortSenden(exchange, 200, response.toJson());
115     } else {
116       HttpResponder r = new HttpResponder();
117       r.antwortSenden(exchange, 406, "Login failed.");
118     }
119   }
120   
121   private void setLoginHeader(HttpExchange exchange) {
122     Headers headers = exchange.getResponseHeaders();
123     headers.add(HttpHelper.CONTENT_TYPE, BEARER_CONTENT_TYPE);
124     headers.add(CACHE_CONTROL, NO_STORE);
125     headers.add(PRAGMA, NO_CACHE);
126   } 
127   
128   private User getUser(HttpExchange exchange) throws IOException {
129     /*
130     Wenn ein JSON-Inhalt im Body uebermittelt wird, steht
131     dort evtl. etwas wie
132     {"name": "fred", "password": "secret"}
133     das kann wie folgt gelesen werden
134      */
135     String body = new HttpHelper().bodyLesen(exchange);
136     Gson gson = new Gson();
137     User user = gson.fromJson(body, User.class);
138     return user;
139   }
140   
141   
142 }