├── .DS_Store ├── .gitignore ├── myoauth.iml ├── src ├── .DS_Store └── com │ ├── .DS_Store │ ├── my │ └── util │ │ ├── HttpURLClient.java │ │ └── URLParamsUtil.java │ └── oauth │ ├── ch03 │ ├── AppIndexServlet.java │ ├── AppServlet.java │ ├── OauthServlet.java │ └── ProtectedServlet.java │ ├── ch04 │ ├── JWTTest.java │ └── RSAJwt.java │ ├── ch06 │ ├── AppIndexServlet.java │ ├── AppServlet.java │ └── OauthServlet.java │ └── ch09 │ ├── AppIndexServlet.java │ ├── AppServlet.java │ ├── OauthServlet.java │ └── ProtectedServlet.java └── web ├── .DS_Store ├── WEB-INF ├── .DS_Store ├── lib │ ├── jackson-annotations-2.10.0.jar │ ├── jackson-core-2.10.0.jar │ ├── jackson-databind-2.10.0.jar │ ├── jjwt-api-0.11.0.jar │ ├── jjwt-impl-0.11.0.jar │ └── jjwt-jackson-0.11.0.jar └── web.xml ├── approve-09.jsp ├── approve.jsp ├── index.jsp └── oidc.jsp /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out -------------------------------------------------------------------------------- /myoauth.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/src/.DS_Store -------------------------------------------------------------------------------- /src/com/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/src/com/.DS_Store -------------------------------------------------------------------------------- /src/com/my/util/HttpURLClient.java: -------------------------------------------------------------------------------- 1 | package com.my.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.OutputStream; 8 | import java.net.HttpURLConnection; 9 | import java.net.MalformedURLException; 10 | import java.net.URL; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public class HttpURLClient { 15 | public static String doGet(String httpurl) { 16 | HttpURLConnection connection = null; 17 | InputStream is = null; 18 | BufferedReader br = null; 19 | String result = null;// 返回结果字符串 20 | try { 21 | // 创建远程url连接对象 22 | URL url = new URL(httpurl); 23 | // 通过远程url连接对象打开一个连接,强转成httpURLConnection类 24 | connection = (HttpURLConnection) url.openConnection(); 25 | // 设置连接方式:get 26 | connection.setRequestMethod("GET"); 27 | // 设置连接主机服务器的超时时间:15000毫秒 28 | connection.setConnectTimeout(15000); 29 | // 设置读取远程返回的数据时间:60000毫秒 30 | connection.setReadTimeout(60000); 31 | // 发送请求 32 | connection.connect(); 33 | // 通过connection连接,获取输入流 34 | if (connection.getResponseCode() == 200) { 35 | is = connection.getInputStream(); 36 | // 封装输入流is,并指定字符集 37 | br = new BufferedReader(new InputStreamReader(is, "UTF-8")); 38 | // 存放数据 39 | StringBuffer sbf = new StringBuffer(); 40 | String temp = null; 41 | while ((temp = br.readLine()) != null) { 42 | sbf.append(temp); 43 | sbf.append("\r\n"); 44 | } 45 | result = sbf.toString(); 46 | } 47 | } catch (MalformedURLException e) { 48 | e.printStackTrace(); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } finally { 52 | // 关闭资源 53 | if (null != br) { 54 | try { 55 | br.close(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | if (null != is) { 61 | try { 62 | is.close(); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | connection.disconnect();// 关闭远程连接 68 | } 69 | return result; 70 | } 71 | 72 | public static String doPost(String httpUrl, String param) { 73 | HttpURLConnection connection = null; 74 | InputStream is = null; 75 | OutputStream os = null; 76 | BufferedReader br = null; 77 | String result = null; 78 | try { 79 | URL url = new URL(httpUrl); 80 | // 通过远程url连接对象打开连接 81 | connection = (HttpURLConnection) url.openConnection(); 82 | // 设置连接请求方式 83 | connection.setRequestMethod("POST"); 84 | // 设置连接主机服务器超时时间:15000毫秒 85 | connection.setConnectTimeout(15000); 86 | // 设置读取主机服务器返回数据超时时间:60000毫秒 87 | connection.setReadTimeout(60000); 88 | // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true 89 | connection.setDoOutput(true); 90 | // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无 91 | connection.setDoInput(true); 92 | // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。 93 | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 94 | // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0 95 | connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0"); 96 | // 通过连接对象获取一个输出流 97 | os = connection.getOutputStream(); 98 | // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 99 | os.write(param.getBytes()); 100 | // 通过连接对象获取一个输入流,向远程读取 101 | if (connection.getResponseCode() == 200) { 102 | is = connection.getInputStream(); 103 | // 对输入流对象进行包装:charset根据工作项目组的要求来设置 104 | br = new BufferedReader(new InputStreamReader(is, "UTF-8")); 105 | StringBuffer sbf = new StringBuffer(); 106 | String temp = null; 107 | // 循环遍历一行一行读取数据 108 | while ((temp = br.readLine()) != null) { 109 | sbf.append(temp); 110 | sbf.append("\r\n"); 111 | } 112 | result = sbf.toString(); 113 | } 114 | } catch (MalformedURLException e) { 115 | e.printStackTrace(); 116 | } catch (IOException e) { 117 | e.printStackTrace(); 118 | } finally { 119 | // 关闭资源 120 | if (null != br) { 121 | try { 122 | br.close(); 123 | } catch (IOException e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | if (null != os) { 128 | try { 129 | os.close(); 130 | } catch (IOException e) { 131 | e.printStackTrace(); 132 | } 133 | } 134 | if (null != is) { 135 | try { 136 | is.close(); 137 | } catch (IOException e) { 138 | e.printStackTrace(); 139 | } 140 | } 141 | // 断开与远程地址url的连接 142 | connection.disconnect(); 143 | } 144 | return result; 145 | } 146 | 147 | public static String appendParams(String url, Map params){ 148 | if(null == url){ 149 | return ""; 150 | }else if(params.isEmpty()){ 151 | return url.trim(); 152 | }else{ 153 | StringBuffer sb = new StringBuffer(""); 154 | Set keys = params.keySet(); 155 | for (String key : keys) { 156 | sb.append(key).append("=").append(params.get(key)).append("&"); 157 | } 158 | sb.deleteCharAt(sb.length() - 1); 159 | 160 | url = url.trim(); 161 | int length = url.length(); 162 | int index = url.indexOf("?"); 163 | if(index > -1){ 164 | if((length - 1) == index){//url最后一个符号为?,如:http://wwww.yy.com? 165 | url += sb.toString(); 166 | }else{//情况为:http://wwww.baidu.com?aa=11 167 | url += "&" + sb.toString(); 168 | } 169 | }else{//url后面没有问号,如:http://wwww.baidu.com 170 | url += "?" + sb.toString(); 171 | } 172 | return url; 173 | } 174 | } 175 | 176 | 177 | public static String mapToStr(Map params){ 178 | StringBuffer sb = new StringBuffer(""); 179 | Set keys = params.keySet(); 180 | for (String key : keys) { 181 | sb.append(key).append("=").append(params.get(key)).append("&"); 182 | } 183 | sb.deleteCharAt(sb.length() - 1); 184 | return sb.toString(); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/com/my/util/URLParamsUtil.java: -------------------------------------------------------------------------------- 1 | package com.my.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | public class URLParamsUtil { 8 | 9 | 10 | 11 | public static String appendParams(String url, Map params){ 12 | if(null == url){ 13 | return ""; 14 | }else if(params.isEmpty()){ 15 | return url.trim(); 16 | }else{ 17 | StringBuffer sb = new StringBuffer(""); 18 | Set keys = params.keySet(); 19 | for (String key : keys) { 20 | sb.append(key).append("=").append(params.get(key)).append("&"); 21 | } 22 | sb.deleteCharAt(sb.length() - 1); 23 | 24 | url = url.trim(); 25 | int length = url.length(); 26 | int index = url.indexOf("?"); 27 | if(index > -1){ 28 | if((length - 1) == index){//url最后一个符号为?,如:http://wwww.yy.com? 29 | url += sb.toString(); 30 | }else{//情况为:http://wwww.baidu.com?aa=11 31 | url += "&" + sb.toString(); 32 | } 33 | }else{//url后面没有问号,如:http://wwww.baidu.com 34 | url += "?" + sb.toString(); 35 | } 36 | return url; 37 | } 38 | } 39 | 40 | 41 | public static String mapToStr(Map params){ 42 | StringBuffer sb = new StringBuffer(""); 43 | Set keys = params.keySet(); 44 | for (String key : keys) { 45 | sb.append(key).append("=").append(params.get(key)).append("&"); 46 | } 47 | sb.deleteCharAt(sb.length() - 1); 48 | return sb.toString(); 49 | } 50 | 51 | 52 | public static void main(String[] args) { 53 | Map params = new HashMap(); 54 | 55 | params.put("code","s3ers89u"); 56 | params.put("grant_type","authorization_code"); 57 | 58 | 59 | 60 | String url="http://localhost/OauthServlet"; 61 | 62 | // System.out.println(URLParamsUtil.appendParams(url,params)); 63 | System.out.println(URLParamsUtil.mapToStr(params)); 64 | 65 | 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/com/oauth/ch03/AppIndexServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch03; 2 | 3 | import com.my.util.URLParamsUtil; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * ** 16 | * 使用此类来模拟【第三方软件的首页】 17 | * 浏览器输入 http://localhost:8080/AppIndexServlet-ch03 18 | */ 19 | @WebServlet("/AppIndexServlet-ch03") 20 | public class AppIndexServlet extends HttpServlet { 21 | 22 | 23 | //8080:三方软件,8081:授权服务,8081:受保护资源服务 为了演示方便我们将授权服务和受保护资源服务放在同一个服务上面 24 | 25 | String oauthUrl = "http://localhost:8081/OauthServlet-ch03?reqType=oauth"; 26 | String redirectUrl = "http://localhost:8080/AppServlet-ch03"; 27 | 28 | 29 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 30 | 31 | } 32 | 33 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 34 | 35 | //授权码许可流程,DEMO CODE 36 | System.out.println("app index ..."); 37 | 38 | Map params = new HashMap(); 39 | params.put("response_type","code"); 40 | params.put("redirect_uri",redirectUrl); 41 | params.put("app_id","APPID_RABBIT"); 42 | params.put("scope","today history"); 43 | 44 | 45 | String toOauthUrl = URLParamsUtil.appendParams(oauthUrl,params);//构造请求授权的URl 46 | 47 | System.out.println("toOauthUrl: "+toOauthUrl); 48 | 49 | response.sendRedirect(toOauthUrl);//授权码流程的【第一次】重定向 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/com/oauth/ch03/AppServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch03; 2 | 3 | import com.my.util.HttpURLClient; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | 15 | /** 16 | * ** 17 | * 使用此类来模拟【第三方软件的Server端】 18 | * 19 | */ 20 | @WebServlet("/AppServlet-ch03") 21 | public class AppServlet extends HttpServlet { 22 | 23 | String oauthURl="http://localhost:8081/OauthServlet-ch03"; 24 | String protectedURl="http://localhost:8081/ProtectedServlet-ch03"; 25 | 26 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 27 | 28 | } 29 | 30 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 31 | 32 | 33 | 34 | //授权码许可流程,DEMO CODE 35 | 36 | String code = request.getParameter("code"); 37 | 38 | Map params = new HashMap(); 39 | params.put("code",code); 40 | params.put("grant_type","authorization_code"); 41 | params.put("app_id","APPID_RABBIT"); 42 | params.put("app_secret","APPSECRET_RABBIT"); 43 | 44 | System.out.println("start post code for token ..."); 45 | String accessToken = HttpURLClient.doPost(oauthURl,HttpURLClient.mapToStr(params)); 46 | 47 | System.out.println("accessToken:"+accessToken); 48 | 49 | //使用 accessToken 请求受保护资源服务 50 | 51 | Map paramsMap = new HashMap(); 52 | 53 | paramsMap.put("app_id","APPID_RABBIT"); 54 | paramsMap.put("app_secret","APPSECRET_RABBIT"); 55 | paramsMap.put("token",accessToken); 56 | 57 | String result = HttpURLClient.doPost(protectedURl,HttpURLClient.mapToStr(paramsMap)); 58 | 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/com/oauth/ch03/OauthServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch03; 2 | 3 | import com.my.util.URLParamsUtil; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Random; 14 | import java.util.UUID; 15 | 16 | /** 17 | * ** 18 | * 使用此类来模拟【授权服务】 19 | */ 20 | 21 | @WebServlet("/OauthServlet-ch03") 22 | public class OauthServlet extends HttpServlet { 23 | 24 | //模拟授权码、令牌等数据存储 25 | static Map codeMap = new HashMap(); 26 | static Map codeScopeMap = new HashMap(); 27 | 28 | static Map tokenMap = new HashMap(); 29 | static Map tokenScopeMap = new HashMap(); 30 | 31 | static Map refreshTokenMap = new HashMap(); 32 | 33 | static Map appMap = new HashMap(); 34 | 35 | static Map reqidMap = new HashMap(); 36 | 37 | 38 | static { 39 | 40 | //模拟第三方软件注册之后的数据库存储 41 | appMap.put("app_id","APPID_RABBIT"); 42 | appMap.put("app_secret","APPSECRET_RABBIT"); 43 | appMap.put("redirect_uri","http://localhost:8080/AppServlet-ch03"); 44 | appMap.put("scope","today history"); 45 | 46 | } 47 | 48 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 49 | 50 | System.out.println("start accept post req, generate access_toen"); 51 | String reqType = request.getParameter("reqType"); 52 | 53 | String grantType = request.getParameter("grant_type"); 54 | String appId = request.getParameter("app_id"); 55 | String appSecret = request.getParameter("app_secret"); 56 | 57 | String responseType = request.getParameter("response_type"); 58 | String redirectUri =request.getParameter("redirect_uri"); 59 | String scope =request.getParameter("scope"); 60 | 61 | //处理用户点击approve按钮动作 62 | if("approve".equals(reqType)){ 63 | String reqid = request.getParameter("reqid");//假设一定能够获取到值 64 | 65 | if(!reqidMap.containsKey(reqid)){ 66 | return; 67 | } 68 | 69 | if("code".equals(responseType)){ 70 | 71 | String[] rscope =request.getParameterValues("rscope"); 72 | 73 | if(!checkScope(rscope)){//验证权限范围,对又验证一次 74 | //超出注册的权限范围 75 | System.out.println("out of scope ..."); 76 | return; 77 | } 78 | 79 | String code = generateCode(appId,"USERTEST");//模拟登陆用户为USERTEST 80 | 81 | codeScopeMap.put(code,rscope);//授权范围与授权码做绑定 82 | 83 | Map params = new HashMap(); 84 | params.put("code",code); 85 | 86 | String toAppUrl = URLParamsUtil.appendParams(redirectUri,params);//构造第三方软件的回调地址,并重定向到该地址 87 | 88 | response.sendRedirect(toAppUrl);//授权码流程的【第二次】重定向 89 | } 90 | } 91 | 92 | //处理授权码流程中的 颁发访问令牌 环节 93 | if("authorization_code".equals(grantType)){ 94 | 95 | if(!appMap.get("app_id").equals(appId)){ 96 | response.getWriter().write("app_id is not available"); 97 | return; 98 | } 99 | 100 | if(!appMap.get("app_secret").equals(appSecret)){ 101 | response.getWriter().write("app_secret is not available"); 102 | return; 103 | } 104 | 105 | String code = request.getParameter("code"); 106 | 107 | if(!isExistCode(code)){//验证code值 108 | return; 109 | } 110 | codeMap.remove(code);//授权码一旦被使用,须要立即作废 111 | 112 | System.out.println("start generate access_toen"); 113 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 114 | tokenScopeMap.put(accessToken,codeScopeMap.get(code));//授权范围与访问令牌绑定 115 | 116 | String refreshToken = generateRefreshToken(appId,"USERTEST");//生成刷新令牌refresh_token的值 117 | 118 | 119 | 120 | // TODO: 2020/2/28 将accessToken和refreshToken做绑定 ,将refreshToken和codeScopeMap做绑定 121 | 122 | response.getWriter().write(accessToken+"|"+refreshToken); 123 | 124 | }else if("refresh_token".equals(grantType)){//处理刷新令牌请求环节 125 | 126 | if(!"APPIDTEST".equals(appId)){ 127 | response.getWriter().write("app_id is not available"); 128 | return; 129 | } 130 | 131 | if(!"APPSECRETTEST".equals(appSecret)){ 132 | response.getWriter().write("app_secret is not available"); 133 | return; 134 | } 135 | 136 | 137 | String refresh_token = request.getParameter("refresh_token"); 138 | 139 | if(!refreshTokenMap.containsKey(refresh_token)){//该refresh_token值不存在 140 | return; 141 | } 142 | 143 | String appStr = refreshTokenMap.get("refresh_token"); 144 | if(!appStr.startsWith(appId+"|"+"USERTEST")){//该refresh_token值 不是颁发给该 第三方软件的 145 | return; 146 | } 147 | 148 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 149 | 150 | // TODO: 2020/2/28 删除旧的access_token 、删除旧的refresh_token、生成新的refresh_token 151 | 152 | response.getWriter().write(accessToken); 153 | 154 | } 155 | 156 | } 157 | 158 | 159 | 160 | 161 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 162 | 163 | 164 | String responseType = request.getParameter("response_type"); 165 | String redirectUri =request.getParameter("redirect_uri"); 166 | String appId = request.getParameter("app_id"); 167 | String scope = request.getParameter("scope"); 168 | 169 | System.out.println("8081 GET responseType: "+responseType); 170 | 171 | if(!appMap.get("app_id").equals(appId)){ 172 | return; 173 | } 174 | 175 | if(!appMap.get("redirect_uri").equals(redirectUri)){ 176 | return; 177 | } 178 | 179 | 180 | //验证第三方软件请求的权限范围是否与当时注册的权限范围一致 181 | if(!checkScope(scope)){ 182 | //超出注册的权限范围 183 | return; 184 | } 185 | 186 | //生成页面reqid 187 | String reqid = String.valueOf(System.currentTimeMillis()); 188 | reqidMap.put(reqid,reqid);//保存该reqid值 189 | 190 | request.setAttribute("reqid",reqid); 191 | request.setAttribute("response_type",responseType); 192 | request.setAttribute("redirect_uri",redirectUri); 193 | request.setAttribute("app_id",appId); 194 | 195 | //跳转到授权页面 196 | request.getRequestDispatcher("/approve.jsp").forward(request,response); 197 | 198 | //至此颁发授权码code的准备工作完毕 199 | 200 | } 201 | 202 | 203 | /** 204 | * 生成code值 205 | * @return 206 | */ 207 | private String generateCode(String appId,String user) { 208 | Random r = new Random(); 209 | StringBuilder strb = new StringBuilder(); 210 | for (int i = 0; i < 8; i++) { 211 | strb.append(r.nextInt(10)); 212 | } 213 | 214 | String code = strb.toString(); 215 | 216 | 217 | codeMap.put(code,appId+"|"+user+"|"+System.currentTimeMillis());//在这一篇章我们仅作为演示用,实际这应该是一个全局内存数据库,有效期官方建议是10分钟 218 | 219 | return code; 220 | } 221 | 222 | 223 | /** 224 | * 生成access_token值 225 | * @param appId 226 | * @param user 227 | * @return 228 | */ 229 | private String generateAccessToken(String appId,String user){ 230 | 231 | String accessToken = UUID.randomUUID().toString(); 232 | 233 | String expires_in = "1";//1天时间过期 234 | 235 | tokenMap.put(accessToken,appId+"|"+user+"|"+System.currentTimeMillis()+"|"+expires_in);//在这一篇章我们仅作为演示用,实际这应该是一个全局数据库,并且有有效期 236 | 237 | return accessToken; 238 | } 239 | 240 | 241 | /** 242 | * 生成refresh_token值 243 | * @param appId 244 | * @param user 245 | * @return 246 | */ 247 | private String generateRefreshToken(String appId,String user){ 248 | 249 | String refreshToken = UUID.randomUUID().toString(); 250 | 251 | refreshTokenMap.put(refreshToken,appId+"|"+user+"|"+System.currentTimeMillis());//在这一篇章我们仅作为演示用,实际这应该是一个全局数据库,并且有有效期 252 | 253 | return refreshToken; 254 | } 255 | 256 | /** 257 | * 是否存在code值 258 | * @param code 259 | * @return 260 | */ 261 | private boolean isExistCode(String code){ 262 | return codeMap.containsKey(code); 263 | } 264 | 265 | /** 266 | * 验证权限 267 | * @param scope 268 | * @return 269 | */ 270 | private boolean checkScope(String scope){ 271 | 272 | System.out.println("appMap size: "+appMap.size()); 273 | System.out.println("appMap scope: "+appMap.get("scope")); 274 | System.out.println("scope: "+scope); 275 | 276 | return appMap.get("scope").contains(scope);//简单模拟权限验证 277 | } 278 | 279 | 280 | /** 281 | * 282 | * @param rscope 283 | * @return 284 | */ 285 | private boolean checkScope(String[] rscope){ 286 | String scope=""; 287 | 288 | for(int i=0; i0){ 38 | queryGoods(""); 39 | } 40 | 41 | if(sbuf.toString().indexOf("add")>0){ 42 | addGoods(""); 43 | } 44 | 45 | if(sbuf.toString().indexOf("del")>0){ 46 | delGoods(""); 47 | } 48 | 49 | //不同的用户对应不同的数据 50 | String user = OauthServlet.tokenMap.get(accessToken); 51 | queryOrders(user); 52 | } 53 | 54 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 55 | 56 | 57 | 58 | 59 | } 60 | 61 | 62 | private String queryGoods(String id){ 63 | return ""; 64 | } 65 | 66 | private boolean addGoods(String goods){ 67 | return true; 68 | } 69 | 70 | private boolean delGoods(String id){ 71 | return true; 72 | } 73 | 74 | private String queryOrders(String user){ 75 | return ""; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/oauth/ch04/JWTTest.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch04; 2 | 3 | import io.jsonwebtoken.*; 4 | import io.jsonwebtoken.security.Keys; 5 | import sun.misc.BASE64Decoder; 6 | 7 | import javax.crypto.spec.SecretKeySpec; 8 | import java.security.*; 9 | import java.security.interfaces.RSAPublicKey; 10 | import java.security.spec.PKCS8EncodedKeySpec; 11 | import java.security.spec.X509EncodedKeySpec; 12 | import java.time.Instant; 13 | import java.time.LocalDateTime; 14 | import java.util.Base64; 15 | import java.util.Calendar; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | public class JWTTest { 20 | 21 | 22 | 23 | /*static String publicKey = "QgkAQIDAQAB"; 24 | static String privateKey = "hellooauth"; 25 | 26 | 27 | public static PublicKey getPublicKey() { 28 | try { 29 | // byte[] keyBytes = Base64.getDecoder().decode(publicKey.getBytes()); 30 | byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(publicKey); // 正确方式 31 | 32 | 33 | RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes)); 34 | 35 | // X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 36 | // KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 37 | return pubKey; 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | return null; 42 | } 43 | 44 | public static PrivateKey getPrivateKey() { 45 | 46 | try { 47 | // byte[] keyBytes = Base64.getDecoder().decode(privateKey.getBytes()); 48 | byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(privateKey); // 正确方式 49 | 50 | PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 51 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 52 | return keyFactory.generatePrivate(keySpec); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | return null; 58 | }*/ 59 | 60 | 61 | public static void main(String[] args) { 62 | String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth"; 63 | Key key = new SecretKeySpec(sharedTokenSecret.getBytes(), 64 | SignatureAlgorithm.HS256.getJcaName()); 65 | 66 | Map headerMap = new HashMap<>(); 67 | headerMap.put("typ", "JWT"); 68 | headerMap.put("alg", "HS256"); 69 | 70 | Map payloadMap = new HashMap<>(); 71 | payloadMap.put("iss", "http://localhost:8081/"); 72 | payloadMap.put("sub", "XIAOMINGTEST"); 73 | payloadMap.put("aud", "APPID_RABBIT"); 74 | payloadMap.put("exp", 1584105790703L); 75 | payloadMap.put("iat", 1584105948372L); 76 | 77 | String jws2 = Jwts.builder().setHeaderParams(headerMap).setClaims(payloadMap).signWith(key,SignatureAlgorithm.HS256).compact(); 78 | 79 | System.out.println("jws2:" + jws2); 80 | 81 | /*String sharedTokenSecret2="hellooauthhellooauthhellooauthhellooaut0"; 82 | Key key2 = new SecretKeySpec(sharedTokenSecret2.getBytes(), 83 | SignatureAlgorithm.HS256.getJcaName()); 84 | 85 | Jws claimsJws = Jwts.parserBuilder().setSigningKey(key2).build().parseClaimsJws(jws2);*/ 86 | 87 | Jws claimsJws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jws2); 88 | 89 | JwsHeader header = claimsJws.getHeader(); 90 | Claims body = claimsJws.getBody(); 91 | 92 | System.out.println("jwt header:" + header); 93 | System.out.println("jwt body:" + body); 94 | System.out.println("jwt sub:" + body.getSubject()); 95 | System.out.println("jwt aud:" + body.getAudience()); 96 | System.out.println("jwt iss:" + body.getIssuer()); 97 | System.out.println("jwt exp:" + body.getExpiration()); 98 | System.out.println("jwt iat:" + body.getIssuedAt()); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/com/oauth/ch04/RSAJwt.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch04; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jws; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | 8 | import java.io.IOException; 9 | import java.security.*; 10 | import java.security.spec.PKCS8EncodedKeySpec; 11 | import java.security.spec.X509EncodedKeySpec; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class RSAJwt { 16 | public static Map map = new HashMap(); 17 | 18 | public static void main(String[] args) throws Exception { 19 | // // 加密 20 | String token = generateToken(1000, "secert123"); 21 | // 解密 22 | decode(token); 23 | } 24 | 25 | public static void decode(String token) throws Exception { 26 | getInfoFromToken(token, map.get("pub")); 27 | 28 | } 29 | 30 | // 打印解密后的数据 31 | public static void getInfoFromToken(String token, byte[] pubKey) throws Exception { 32 | Jws claimsJws = parserToken(token, pubKey); 33 | Claims body = claimsJws.getBody(); 34 | System.out.println("body:" + body.getSubject()); 35 | System.out.println("id:" + body.get("id")); 36 | System.out.println("name:" + body.get("name")); 37 | } 38 | 39 | // 解析加密过的token,解析成Claims,就是自定义的playload部分 40 | public static Jws parserToken(String token, byte[] pubKey) throws Exception { 41 | Jws claimsJws = Jwts.parser().setSigningKey(getPublicKey(pubKey)).parseClaimsJws(token); 42 | return claimsJws; 43 | } 44 | 45 | /*** 46 | * 47 | * @param expire 过期事件 48 | * @param secert 盐 49 | */ 50 | public static String generateToken(int expire, String secert) throws Exception { 51 | Map key = generateKey(secert); 52 | 53 | /*String compactJws = Jwts.builder() 54 | .setSubject("subject2") 55 | // 添加clain,就是自定义的playload部分 56 | .claim("id", "1") 57 | .claim("name", "zhangsan") 58 | .setExpiration(DateTime.now().plusSeconds(expire).toDate()) 59 | .signWith(SignatureAlgorithm.RS256, getPrivateKey(key.get("pri"))) 60 | .compact();*/ 61 | 62 | Map headerMap = new HashMap<>(); 63 | headerMap.put("typ", "JWT"); 64 | headerMap.put("alg", "HS256"); 65 | 66 | Map payloadMap = new HashMap<>(); 67 | payloadMap.put("sub", "Tom"); 68 | payloadMap.put("iss", "--"); 69 | 70 | String compactJws = Jwts.builder().setHeaderParams(headerMap).setClaims(payloadMap).signWith(getPrivateKey(key.get("pri")), SignatureAlgorithm.HS256).compact(); 71 | 72 | System.out.println(compactJws); 73 | return compactJws; 74 | } 75 | 76 | /** 77 | * 获取公钥 78 | * 79 | * @param publicKey 80 | * @return 81 | * @throws Exception 82 | */ 83 | // 使用jdk 的security把byte数据解析成getPublicKey类 84 | public static PublicKey getPublicKey(byte[] publicKey) throws Exception { 85 | X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey); 86 | KeyFactory kf = KeyFactory.getInstance("RSA"); 87 | return kf.generatePublic(spec); 88 | } 89 | 90 | // 使用jdk 的security把byte数据解析成PrivateKey类 91 | public static PrivateKey getPrivateKey(byte[] privateKey) throws Exception { 92 | PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKey); 93 | KeyFactory kf = KeyFactory.getInstance("RSA"); 94 | return kf.generatePrivate(spec); 95 | } 96 | 97 | // 使用jdk的security生成rsa的公钥和私钥 98 | public static Map generateKey(String password) throws IOException, NoSuchAlgorithmException { 99 | KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 100 | SecureRandom secureRandom = new SecureRandom(password.getBytes()); 101 | keyPairGenerator.initialize(1024, secureRandom); 102 | KeyPair keyPair = keyPairGenerator.genKeyPair(); 103 | byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); 104 | byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); 105 | map.put("pub", publicKeyBytes); 106 | map.put("pri", privateKeyBytes); 107 | return map; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/com/oauth/ch06/AppIndexServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch06; 2 | 3 | import com.my.util.URLParamsUtil; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @WebServlet("/AppIndexServlet-ch02") 15 | public class AppIndexServlet extends HttpServlet { 16 | 17 | String oauthUrl = "http://localhost:8081/OauthServlet-ch02"; 18 | String redirectUrl = "http://localhost:8080/AppServlet-ch02"; 19 | 20 | 21 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 22 | 23 | } 24 | 25 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 26 | 27 | 28 | //授权码许可流程,DEMO CODE 29 | /*System.out.println("app index ..."); 30 | Map params = new HashMap(); 31 | params.put("response_type","code"); 32 | params.put("redirect_uri","http://localhost:8080/AppServlet-ch02"); 33 | params.put("app_id","APPIDTEST"); 34 | 35 | 36 | String toOauthUrl = URLParamsUtil.appendParams(oauthUrl,params);//构造请求授权的URl 37 | 38 | System.out.println("toOauthUrl: "+toOauthUrl); 39 | 40 | response.sendRedirect(toOauthUrl);//授权码流程的第一次重定向 41 | 42 | //response.sendRedirect("http://localhost:8081/OauthServlet-ch02?response_type=code&redirect_uri=http://localhost:8080/AppServlet-ch02&app_id=APPIDTEST"); 43 | */ 44 | //隐式许可流程(模拟),DEMO CODE 45 | Map params = new HashMap(); 46 | params.put("response_type","token");//告诉授权服务直接返回access_token 47 | params.put("redirect_uri","http://localhost:8080/AppServlet-ch02"); 48 | params.put("app_id","APPIDTEST"); 49 | 50 | String toOauthUrl = URLParamsUtil.appendParams(oauthUrl,params);//构造请求授权的URl 51 | 52 | response.sendRedirect(toOauthUrl); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/oauth/ch06/AppServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch06; 2 | 3 | import com.my.util.HttpURLClient; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @WebServlet("/AppServlet-ch02") 15 | public class AppServlet extends HttpServlet { 16 | 17 | 18 | 19 | String oauthURl="http://localhost:8081/OauthServlet-ch02"; 20 | 21 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 22 | 23 | } 24 | 25 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 26 | 27 | 28 | //授权码许可流程,DEMO CODE 29 | /*System.out.println("start parse code..."); 30 | 31 | String code = request.getParameter("code"); 32 | 33 | Map params = new HashMap(); 34 | params.put("code",code); 35 | params.put("grant_type","authorization_code"); 36 | params.put("app_id","APPIDTEST"); 37 | params.put("app_secret","APPSECRETTEST"); 38 | 39 | String accessToken = HttpURLClient.doPost(oauthURl,HttpURLClient.mapToStr(params)); 40 | 41 | System.out.println("accessToken:"+accessToken);*/ 42 | 43 | 44 | //隐式许可流程(模拟),DEMO CODE 45 | /*String accessToken = request.getParameter("access_token"); 46 | System.out.println("accessToken:"+accessToken);*/ 47 | 48 | //第三方软件凭据许可流程,DEMO CODE 49 | /*Map params = new HashMap(); 50 | params.put("grant_type","client_credentials"); 51 | params.put("app_id","APPIDTEST"); 52 | params.put("app_secret","APPSECRETTEST"); 53 | 54 | String accessToken = HttpURLClient.doPost(oauthURl,HttpURLClient.mapToStr(params)); 55 | System.out.println("accessToken:"+accessToken);*/ 56 | 57 | 58 | //资源拥有者凭据许可流程,DEMO CODE 59 | Map params = new HashMap(); 60 | params.put("grant_type","password"); 61 | params.put("app_id","APPIDTEST"); 62 | params.put("app_secret","APPSECRETTEST"); 63 | params.put("name","NAMETEST"); 64 | params.put("password","PASSWORDTEST"); 65 | 66 | String accessToken = HttpURLClient.doPost(oauthURl,HttpURLClient.mapToStr(params)); 67 | System.out.println("accessToken:"+accessToken); 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/com/oauth/ch06/OauthServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch06; 2 | 3 | import com.my.util.URLParamsUtil; 4 | 5 | import javax.servlet.annotation.WebServlet; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Random; 13 | import java.util.UUID; 14 | 15 | @WebServlet("/OauthServlet-ch02") 16 | public class OauthServlet extends HttpServlet { 17 | 18 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 19 | 20 | System.out.println("start accept post req, generate access_toen"); 21 | 22 | String grantType = request.getParameter("grant_type"); 23 | String appId = request.getParameter("app_id"); 24 | 25 | if(!"APPIDTEST".equals(appId)){ 26 | response.getWriter().write("app_id is not available"); 27 | return; 28 | } 29 | 30 | 31 | if("authorization_code".equals(grantType)){ 32 | 33 | String code = request.getParameter("code"); 34 | if(null == request.getSession().getAttribute(code)){//验证code值 35 | return; 36 | } 37 | 38 | System.out.println("start generate access_toen"); 39 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 40 | 41 | response.getWriter().write(accessToken); 42 | 43 | }else if("client_credentials".equals(grantType)){ 44 | String appSecret = request.getParameter("app_secret"); 45 | 46 | if(!"APPSECRETTEST".equals(appSecret)){ 47 | response.getWriter().write("app_secret is not available"); 48 | return; 49 | } 50 | 51 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 52 | 53 | response.getWriter().write(accessToken); 54 | 55 | }else if("password".equals(grantType)){ 56 | String appSecret = request.getParameter("app_secret"); 57 | String username = request.getParameter("username"); 58 | String password = request.getParameter("password"); 59 | 60 | if(!"APPSECRETTEST".equals(appSecret)){ 61 | response.getWriter().write("app_secret is not available"); 62 | return; 63 | } 64 | if(!"USERNAMETEST".equals(username)){ 65 | response.getWriter().write("username is not available"); 66 | return; 67 | } 68 | 69 | if(!"PASSWORDTEST".equals(password)){ 70 | response.getWriter().write("password is not available"); 71 | return; 72 | } 73 | 74 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 75 | 76 | response.getWriter().write(accessToken); 77 | 78 | }else if("refresh_token".equals(grantType)){ 79 | 80 | 81 | }else{ 82 | 83 | } 84 | 85 | 86 | } 87 | 88 | 89 | 90 | 91 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 92 | 93 | String responseType = request.getParameter("response_type"); 94 | String redirectUri =request.getParameter("redirect_uri"); 95 | String appId = request.getParameter("app_id"); 96 | // String appSecret = request.getParameter("app_secret"); 97 | // String code = request.getParameter("code"); 98 | // String accessToken = request.getParameter("access_token"); 99 | // String refreshToken = request.getParameter("refresh_token"); 100 | // String scop = request.getParameter("scop"); 101 | // String grantType = request.getParameter("grant_type"); 102 | // String tokenType = request.getParameter("token_type"); 103 | // String state = request.getParameter("state"); 104 | 105 | if(!"APPIDTEST".equals(appId)){ 106 | return; 107 | } 108 | 109 | if("code".equals(responseType)){ 110 | //授权码许可流程,DEMO CODE 111 | System.out.println("oauth accept code req..."); 112 | 113 | String code = generateCode(appId,"USERTEST",request);//生成code值 114 | Map params = new HashMap(); 115 | params.put("code",code); 116 | 117 | String toAppUrl = URLParamsUtil.appendParams(redirectUri,params);//构造第三方软件的回调地址,并重定向到该地址 118 | 119 | System.out.println("toAppUrl: "+toAppUrl); 120 | 121 | response.sendRedirect(toAppUrl); 122 | 123 | 124 | }else if("token".equals(responseType)){ 125 | //隐式许可流程(模拟),DEMO CODE,注意 该流程全是在前端通信中完成的 126 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 127 | 128 | Map params = new HashMap(); 129 | params.put("redirect_uri",redirectUri); 130 | params.put("access_token",accessToken); 131 | 132 | String toAppUrl = URLParamsUtil.appendParams(redirectUri,params);//构造第三方软件的回调地址,并重定向到该地址 133 | 134 | System.out.println("toAppUrl: "+toAppUrl); 135 | 136 | response.sendRedirect(toAppUrl);//使用sendRedirect方式模拟前端通信 137 | 138 | } 139 | 140 | 141 | 142 | 143 | } 144 | 145 | 146 | /** 147 | * 生成code值 148 | * @return 149 | */ 150 | private String generateCode(String appId,String user,HttpServletRequest request) { 151 | Random r = new Random(); 152 | StringBuilder strb = new StringBuilder(); 153 | for (int i = 0; i < 8; i++) { 154 | strb.append(r.nextInt(10)); 155 | } 156 | 157 | String code = strb.toString(); 158 | 159 | 160 | request.getSession().setAttribute(code,appId+"|"+user);//在这一篇章我们仅作为演示用,实际这应该是一个全局内存数据库,有效期官方建议是10分钟 161 | 162 | return code; 163 | } 164 | 165 | 166 | /** 167 | * 生成access_token值 168 | * @param appId 169 | * @param user 170 | * @return 171 | */ 172 | private String generateAccessToken(String appId,String user){ 173 | 174 | String accessToken = UUID.randomUUID().toString(); 175 | 176 | Map map = new HashMap();//在这一篇章我们仅作为演示用,实际这应该是一个全局数据库,并且有有效期 177 | map.put(accessToken,appId+"|"+user); 178 | 179 | return accessToken; 180 | } 181 | 182 | 183 | public static void main(String[] args) { 184 | 185 | // System.out.println(new OauthServlet().generateCode()); 186 | System.out.println(UUID.randomUUID()); 187 | 188 | } 189 | 190 | } 191 | -------------------------------------------------------------------------------- /src/com/oauth/ch09/AppIndexServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch09; 2 | 3 | import com.my.util.URLParamsUtil; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.annotation.WebServlet; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * ** 16 | * 使用此类来模拟【第三方软件的首页】 17 | * 浏览器输入 http://localhost:8080/AppIndexServlet-ch09 18 | */ 19 | @WebServlet("/AppIndexServlet-ch09") 20 | public class AppIndexServlet extends HttpServlet { 21 | 22 | //8080:三方软件,8081:授权服务,8081:受保护资源服务 为了演示方便我们将授权服务和受保护资源服务放在同一个服务上面 23 | 24 | String oauthUrl = "http://localhost:8081/OauthServlet-ch09?reqType=oauth"; 25 | String redirectUrl = "http://localhost:8080/AppServlet-ch09"; 26 | 27 | 28 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 | 30 | } 31 | 32 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 33 | 34 | //授权码许可流程,DEMO CODE 35 | System.out.println("app index ..."); 36 | 37 | Map params = new HashMap(); 38 | params.put("response_type","code"); 39 | params.put("redirect_uri",redirectUrl); 40 | params.put("app_id","APPID_RABBIT"); 41 | params.put("scope","today history"); 42 | 43 | 44 | String toOauthUrl = URLParamsUtil.appendParams(oauthUrl,params);//构造请求授权的URl 45 | 46 | System.out.println("toOauthUrl: "+toOauthUrl); 47 | 48 | response.sendRedirect(toOauthUrl);//授权码流程的【第一次】重定向 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/oauth/ch09/AppServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch09; 2 | 3 | import com.my.util.HttpURLClient; 4 | import io.jsonwebtoken.*; 5 | 6 | import javax.crypto.spec.SecretKeySpec; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | import java.security.Key; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | 18 | /** 19 | * ** 20 | * 使用此类来模拟【第三方软件的Server端】 21 | * 22 | */ 23 | @WebServlet("/AppServlet-ch09") 24 | public class AppServlet extends HttpServlet { 25 | 26 | String oauthURl="http://localhost:8081/OauthServlet-ch09"; 27 | String protectedURl="http://localhost:8081/ProtectedServlet-ch09"; 28 | 29 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 30 | 31 | } 32 | 33 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 34 | 35 | //授权码许可流程,DEMO CODE 36 | 37 | String code = request.getParameter("code"); 38 | 39 | Map params = new HashMap(); 40 | params.put("code",code); 41 | params.put("grant_type","authorization_code"); 42 | params.put("app_id","APPID_RABBIT"); 43 | params.put("app_secret","APPSECRET_RABBIT"); 44 | 45 | System.out.println("start post code for token ..."); 46 | String result = HttpURLClient.doPost(oauthURl,HttpURLClient.mapToStr(params)); 47 | 48 | System.out.println("result:"+result); 49 | String[] arry = result.split("&"); 50 | String accessToken = arry[0]; 51 | String id_token = arry[1]; 52 | 53 | System.out.println("accessToken:"+accessToken); 54 | System.out.println("id_token:"+id_token); 55 | 56 | //获取用户登录标识 57 | Map map = parseJwt(id_token); 58 | 59 | request.setAttribute("sub",map.get("sub")); 60 | 61 | //跳转到授权页面 62 | request.getRequestDispatcher("/oidc.jsp").forward(request,response); 63 | 64 | 65 | /*//使用 accessToken 请求受保护资源服务 66 | Map paramsMap = new HashMap(); 67 | 68 | paramsMap.put("app_id","APPID_RABBIT"); 69 | paramsMap.put("app_secret","APPSECRET_RABBIT"); 70 | paramsMap.put("token",accessToken); 71 | 72 | HttpURLClient.doPost(protectedURl,HttpURLClient.mapToStr(paramsMap)); 73 | */ 74 | 75 | } 76 | 77 | private Map parseJwt(String jwt){ 78 | String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth"; 79 | Key key = new SecretKeySpec(sharedTokenSecret.getBytes(), 80 | SignatureAlgorithm.HS256.getJcaName()); 81 | 82 | Map map = new HashMap(); 83 | 84 | Jws claimsJws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwt); 85 | 86 | // JwsHeader header = claimsJws.getHeader(); 87 | Claims body = claimsJws.getBody(); 88 | 89 | // System.out.println("jwt header:" + header); 90 | System.out.println("jwt body:" + body); 91 | 92 | map.put("sub",body.getSubject()); 93 | map.put("aud",body.getAudience()); 94 | map.put("iss",body.getIssuer()); 95 | 96 | return map; 97 | } 98 | 99 | 100 | public static void main(String[] args) { 101 | 102 | String ss="95fd88bc-c69e-4add-bf3a-5d75766b85a7&eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJYSUFPTUlOR1RFU1QiLCJhdWQiOiJBUFBJRF9SQUJCSVQiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODEvIiwiZXhwIjoxNTg0MTA1NzkwNzAzLCJpYXQiOjE1ODQxMDU5NDgzNzJ9.SoJT62wYOMihpaH3Ttxf3WYwnC6qEyKbJ-bF7jMqxL8"; 103 | 104 | String[] arry = ss.split("&"); 105 | System.out.println("access_token:"+arry[0]); 106 | System.out.println("id_token:"+arry[1]); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/com/oauth/ch09/OauthServlet.java: -------------------------------------------------------------------------------- 1 | package com.oauth.ch09; 2 | 3 | import com.my.util.URLParamsUtil; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | 7 | import javax.crypto.spec.SecretKeySpec; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | import java.security.Key; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.Random; 18 | import java.util.UUID; 19 | 20 | @WebServlet("/OauthServlet-ch09") 21 | public class OauthServlet extends HttpServlet { 22 | 23 | //模拟授权码、令牌等数据存储 24 | static Map codeMap = new HashMap(); 25 | static Map codeScopeMap = new HashMap(); 26 | 27 | static Map tokenMap = new HashMap(); 28 | static Map tokenScopeMap = new HashMap(); 29 | 30 | static Map refreshTokenMap = new HashMap(); 31 | 32 | static Map appMap = new HashMap(); 33 | 34 | static Map reqidMap = new HashMap(); 35 | 36 | 37 | static { 38 | 39 | //模拟第三方软件注册之后的数据库存储 40 | appMap.put("app_id","APPID_RABBIT"); 41 | appMap.put("app_secret","APPSECRET_RABBIT"); 42 | appMap.put("redirect_uri","http://localhost:8080/AppServlet-ch09"); 43 | appMap.put("scope","today history"); 44 | 45 | } 46 | 47 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 48 | 49 | System.out.println("start accept post req, generate access_toen"); 50 | String reqType = request.getParameter("reqType"); 51 | 52 | String grantType = request.getParameter("grant_type"); 53 | String appId = request.getParameter("app_id"); 54 | String appSecret = request.getParameter("app_secret"); 55 | 56 | String responseType = request.getParameter("response_type"); 57 | String redirectUri =request.getParameter("redirect_uri"); 58 | String scope =request.getParameter("scope"); 59 | 60 | //处理用户点击approve按钮动作 61 | if("approve".equals(reqType)){ 62 | String reqid = request.getParameter("reqid");//假设一定能够获取到值 63 | 64 | if(!reqidMap.containsKey(reqid)){ 65 | return; 66 | } 67 | 68 | if("code".equals(responseType)){ 69 | 70 | String[] rscope =request.getParameterValues("rscope"); 71 | 72 | if(!checkScope(rscope)){//验证权限范围,对又验证一次 73 | //超出注册的权限范围 74 | System.out.println("out of scope ..."); 75 | return; 76 | } 77 | 78 | String code = generateCode(appId,"USERTEST");//模拟登陆用户为USERTEST 79 | 80 | codeScopeMap.put(code,rscope);//授权范围与授权码做绑定 81 | 82 | Map params = new HashMap(); 83 | params.put("code",code); 84 | 85 | String toAppUrl = URLParamsUtil.appendParams(redirectUri,params);//构造第三方软件的回调地址,并重定向到该地址 86 | 87 | response.sendRedirect(toAppUrl);//授权码流程的【第二次】重定向 88 | } 89 | } 90 | 91 | //处理授权码流程中的 颁发访问令牌 环节 92 | if("authorization_code".equals(grantType)){ 93 | 94 | if(!appMap.get("app_id").equals(appId)){ 95 | response.getWriter().write("app_id is not available"); 96 | return; 97 | } 98 | 99 | if(!appMap.get("app_secret").equals(appSecret)){ 100 | response.getWriter().write("app_secret is not available"); 101 | return; 102 | } 103 | 104 | String code = request.getParameter("code"); 105 | 106 | if(!isExistCode(code)){//验证code值 107 | return; 108 | } 109 | codeMap.remove(code);//授权码一旦被使用,须要立即作废 110 | 111 | System.out.println("start generate access_toen"); 112 | String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值 113 | tokenScopeMap.put(accessToken,codeScopeMap.get(code));//授权范围与访问令牌绑定 114 | 115 | //GENATE ID TOKEN 116 | String id_token=genrateIdToken(appId,"XIAOMINGTEST");//模拟用户小明登录 117 | 118 | response.getWriter().write(accessToken+"&"+id_token); 119 | 120 | } 121 | 122 | } 123 | 124 | 125 | /** 126 | * genrate 127 | * @param appId 128 | * @param user 129 | * @return 130 | */ 131 | private String genrateIdToken(String appId,String user){ 132 | String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth"; 133 | Key key = new SecretKeySpec(sharedTokenSecret.getBytes(), 134 | SignatureAlgorithm.HS256.getJcaName()); 135 | 136 | Map headerMap = new HashMap<>(); 137 | headerMap.put("typ", "JWT"); 138 | headerMap.put("alg", "HS256"); 139 | 140 | Map payloadMap = new HashMap<>(); 141 | payloadMap.put("iss", "http://localhost:8081/"); 142 | payloadMap.put("sub", user); 143 | payloadMap.put("aud", appId); 144 | payloadMap.put("exp", 1584105790703L); 145 | payloadMap.put("iat", 1584105948372L); 146 | 147 | return Jwts.builder().setHeaderParams(headerMap).setClaims(payloadMap).signWith(key,SignatureAlgorithm.HS256).compact(); 148 | } 149 | 150 | 151 | 152 | 153 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 154 | 155 | 156 | String responseType = request.getParameter("response_type"); 157 | String redirectUri =request.getParameter("redirect_uri"); 158 | String appId = request.getParameter("app_id"); 159 | String scope = request.getParameter("scope"); 160 | 161 | System.out.println("8081 GET responseType: "+responseType); 162 | System.out.println("8081 GET redirect_uri: "+redirectUri); 163 | System.out.println("8081 GET app_id: "+appId); 164 | System.out.println("8081 GET scope: "+scope); 165 | 166 | if(!appMap.get("app_id").equals(appId)){ 167 | return; 168 | } 169 | 170 | if(!appMap.get("redirect_uri").equals(redirectUri)){ 171 | return; 172 | } 173 | 174 | 175 | //验证第三方软件请求的权限范围是否与当时注册的权限范围一致 176 | if(!checkScope(scope)){ 177 | //超出注册的权限范围 178 | return; 179 | } 180 | 181 | //生成页面reqid 182 | String reqid = String.valueOf(System.currentTimeMillis()); 183 | reqidMap.put(reqid,reqid);//保存该reqid值 184 | 185 | request.setAttribute("reqid",reqid); 186 | request.setAttribute("response_type",responseType); 187 | request.setAttribute("redirect_uri",redirectUri); 188 | request.setAttribute("app_id",appId); 189 | 190 | //跳转到授权页面 191 | request.getRequestDispatcher("/approve-09.jsp").forward(request,response); 192 | 193 | //至此颁发授权码code的准备工作完毕 194 | 195 | } 196 | 197 | 198 | /** 199 | * 生成code值 200 | * @return 201 | */ 202 | private String generateCode(String appId,String user) { 203 | Random r = new Random(); 204 | StringBuilder strb = new StringBuilder(); 205 | for (int i = 0; i < 8; i++) { 206 | strb.append(r.nextInt(10)); 207 | } 208 | 209 | String code = strb.toString(); 210 | 211 | 212 | codeMap.put(code,appId+"|"+user+"|"+System.currentTimeMillis());//在这一篇章我们仅作为演示用,实际这应该是一个全局内存数据库,有效期官方建议是10分钟 213 | 214 | return code; 215 | } 216 | 217 | 218 | /** 219 | * 生成access_token值 220 | * @param appId 221 | * @param user 222 | * @return 223 | */ 224 | private String generateAccessToken(String appId,String user){ 225 | 226 | String accessToken = UUID.randomUUID().toString(); 227 | 228 | String expires_in = "1";//1天时间过期 229 | 230 | tokenMap.put(accessToken,appId+"|"+user+"|"+System.currentTimeMillis()+"|"+expires_in);//在这一篇章我们仅作为演示用,实际这应该是一个全局数据库,并且有有效期 231 | 232 | return accessToken; 233 | } 234 | 235 | 236 | /** 237 | * 生成refresh_token值 238 | * @param appId 239 | * @param user 240 | * @return 241 | */ 242 | private String generateRefreshToken(String appId,String user){ 243 | 244 | String refreshToken = UUID.randomUUID().toString(); 245 | 246 | refreshTokenMap.put(refreshToken,appId+"|"+user+"|"+System.currentTimeMillis());//在这一篇章我们仅作为演示用,实际这应该是一个全局数据库,并且有有效期 247 | 248 | return refreshToken; 249 | } 250 | 251 | /** 252 | * 是否存在code值 253 | * @param code 254 | * @return 255 | */ 256 | private boolean isExistCode(String code){ 257 | return codeMap.containsKey(code); 258 | } 259 | 260 | /** 261 | * 验证权限 262 | * @param scope 263 | * @return 264 | */ 265 | private boolean checkScope(String scope){ 266 | 267 | System.out.println("appMap size: "+appMap.size()); 268 | System.out.println("appMap scope: "+appMap.get("scope")); 269 | System.out.println("scope: "+scope); 270 | 271 | return appMap.get("scope").contains(scope);//简单模拟权限验证 272 | } 273 | 274 | 275 | /** 276 | * 277 | * @param rscope 278 | * @return 279 | */ 280 | private boolean checkScope(String[] rscope){ 281 | String scope=""; 282 | 283 | for(int i=0; i0){ 36 | queryGoods(""); 37 | } 38 | 39 | if(sbuf.toString().indexOf("add")>0){ 40 | addGoods(""); 41 | } 42 | 43 | if(sbuf.toString().indexOf("del")>0){ 44 | delGoods(""); 45 | } 46 | 47 | //不同的用户对应不同的数据 48 | String user = OauthServlet.tokenMap.get(accessToken); 49 | queryOrders(user); 50 | } 51 | 52 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 53 | 54 | 55 | 56 | 57 | } 58 | 59 | 60 | private String queryGoods(String id){ 61 | return ""; 62 | } 63 | 64 | private boolean addGoods(String goods){ 65 | return true; 66 | } 67 | 68 | private boolean delGoods(String id){ 69 | return true; 70 | } 71 | 72 | private String queryOrders(String user){ 73 | return ""; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /web/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/.DS_Store -------------------------------------------------------------------------------- /web/WEB-INF/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/.DS_Store -------------------------------------------------------------------------------- /web/WEB-INF/lib/jackson-annotations-2.10.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jackson-annotations-2.10.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/lib/jackson-core-2.10.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jackson-core-2.10.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/lib/jackson-databind-2.10.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jackson-databind-2.10.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/lib/jjwt-api-0.11.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jjwt-api-0.11.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/lib/jjwt-impl-0.11.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jjwt-impl-0.11.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/lib/jjwt-jackson-0.11.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xindongbook/oauth2-code/cfdc7bf1d27939c1648f9de5a1c31c956c371577/web/WEB-INF/lib/jjwt-jackson-0.11.0.jar -------------------------------------------------------------------------------- /web/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /web/approve-09.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 3 | 4 | 5 | Oauth Test 6 | 7 | 8 | 9 |
10 | " /> 11 | " /> 12 | " /> 13 | " /> 14 | 15 | 16 | 17 | 18 | Are you sure you want the authorization code? 19 | 20 |
21 | appid: <%=request.getAttribute("app_id")%> 22 | 23 |
24 | today
25 | history
26 | <%--pic
--%> 27 | 28 |
29 | 30 | 31 |
32 | 33 | 34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /web/approve.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 3 | 4 | 5 | Oauth Test 6 | 7 | 8 | 9 |
10 | " /> 11 | " /> 12 | " /> 13 | " /> 14 | 15 | 16 | 17 | 18 | Are you sure you want the authorization code? 19 | 20 |
21 | appid: <%=request.getAttribute("app_id")%> 22 | 23 |
24 | today
25 | history
26 | <%--pic
--%> 27 | 28 |
29 | 30 | 31 |
32 | 33 | 34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /web/index.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: wangxindong 4 | Date: 2020/2/16 5 | Time: 上午11:27 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 9 | 10 | 11 | OAuth 2 code test 12 | 13 | 14 | OAuth 2 code test 15 | please go to ch03 or ch09 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/oidc.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 3 | 4 | 5 | Oauth Test 6 | 7 | 8 | 9 | hello ,<%=request.getAttribute("sub")%> ,you have signed in successfully。 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------