1. 程式人生 > >使用JWT保護你的Spring Boot應用

使用JWT保護你的Spring Boot應用

作者 freewolf

原創文章轉載請標明出處
關鍵詞
Spring Boot、OAuth 2.0、JWT、Spring Security、SSO、UAA

寫在前面
最近安靜下來,重新學習一些東西,最近一年幾乎沒寫過程式碼。整天疲於奔命的日子終於結束了。坐下來,弄杯咖啡,思考一些問題,挺好。這幾天有人問我Spring Boot結合Spring Security實現OAuth認證的問題,寫了個Demo,順便分享下。Spring 2之後就沒再用過Java,主要是xml太麻煩,就投入了Node.js的懷抱,現在Java倒是好過之前很多,無論是執行效率還是其他什麼。感謝Pivotal團隊在Spring boot上的努力,感謝Josh Long,一個有意思的攻城獅。

我又搞Java也是為了去折騰微服務,因為目前看國內就Java程式猿最好找,雖然水平好的難找,但是至少能找到,不像其他程式語言,找個會世界上最好的程式語言PHP的人真的不易。

Spring Boot
有了Spring Boot這樣的神器,可以很簡單的使用強大的Spring框架。你需要關心的事兒只是建立應用,不必再配置了,“Just run!”,這可是Josh Long每次演講必說的,他的另一句必須說的就是“make jar not war”,這意味著,不用太關心是Tomcat還是Jetty或者Undertow了。專心解決邏輯問題,這當然是個好事兒,部署簡單了很多。

建立Spring Boot應用
有很多方法去建立Spring Boot專案,官方也推薦用:

Spring Boot線上專案建立
CLI 工具
start.spring.io可以方便選擇你要用的元件,命令列工具當然也可以。目前Spring Boot已經到了1.53,我是懶得去更新依賴,繼續用1.52版本。雖然阿里也有了中央庫的國內版本不知道是否穩定。如果你感興趣,可以自己嘗試下。你可以選Maven或者Gradle成為你專案的構建工具,Gradle優雅一些,使用了Groovy語言進行描述。

開啟start.spring.io,建立的專案只需要一個Dependency,也就是Web,然後下載專案,用IntellJ IDEA開啟。我的Java版本是1.8。

這裡看下整個專案的pom.xml檔案中的依賴部分:



org.springframework.boot
spring-boot-starter-web

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>


所有Spring Boot相關的依賴都是以starter形式出現,這樣你無需關心版本和相關的依賴,所以這樣大大簡化了開發過程。

當你在pom檔案中集成了spring-boot-maven-plugin外掛後你可以使用Maven相關的命令來run你的應用。例如mvn spring-boot:run,這樣會啟動一個嵌入式的Tomcat,並執行在8080埠,直接訪問你當然會獲得一個Whitelabel Error Page,這說明Tomcat已經啟動了。

建立一個Web 應用
這還是一篇關於Web安全的文章,但是也得先有個簡單的HTTP請求響應。我們先弄一個可以返回JSON的Controller。修改程式的入口檔案:

@SpringBootApplication
@RestController
@EnableAutoConfiguration
public class DemoApplication {

// main函式,Spring Boot程式入口
public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}

// 根目錄對映 Get訪問方式 直接返回一個字串
@RequestMapping("/")
Map<String, String> hello() {
  // 返回map會變成JSON key value方式
  Map<String,String> map=new HashMap<String,String>();
  map.put("content", "hello freewolf~");
  return map;
}

}
這裡我儘量的寫清楚,讓不瞭解Spring Security的人通過這個例子可以瞭解這個東西,很多人都覺得它很複雜,而投向了Apache Shiro,其實這個並不難懂。知道主要的處理流程,和這個流程中哪些類都起了哪些作用就好了。

Spring Boot對於開發人員最大的好處在於可以對Spring應用進行自動配置。Spring Boot會根據應用中宣告的第三方依賴來自動配置Spring框架,而不需要進行顯式的宣告。Spring Boot推薦採用基於Java註解的配置方式,而不是傳統的XML。只需要在主配置 Java 類上新增@EnableAutoConfiguration註解就可以啟用自動配置。Spring Boot的自動配置功能是沒有侵入性的,只是作為一種基本的預設實現。

這個入口類我們新增@RestController和@EnableAutoConfiguration兩個註解。
@RestController註解相當於@ResponseBody和@Controller合在一起的作用。

run整個專案。訪問http://localhost:8080/就能看到這個JSON的輸出。使用Chrome瀏覽器可以裝JSON Formatter這個外掛,顯示更PL一些。

{
“content”: “hello freewolf~”
}
為了顯示統一的JSON返回,這裡建立一個JSONResult類進行,簡單的處理。首先修改pom.xml,加入org.json相關依賴。


org.json
json

然後在我們的程式碼中加入一個新的類,裡面只有一個結果集處理方法,因為只是個Demo,所有這裡都放在一個檔案中。這個類只是讓返回的JSON結果變為三部分:

status - 返回狀態碼 0 代表正常返回,其他都是錯誤
message - 一般顯示錯誤資訊
result - 結果集
class JSONResult{
public static String fillResultString(Integer status, String message, Object result){
JSONObject jsonObject = new JSONObject(){{
put(“status”, status);
put(“message”, message);
put(“result”, result);
}};
return jsonObject.toString();
}
}
然後我們引入一個新的@RestController並返回一些簡單的結果,後面我們將對這些內容進行訪問控制,這裡用到了上面的結果集處理類。這裡多放兩個方法,後面我們來測試許可權和角色的驗證用。

@RestController
class UserController {

// 路由對映到/users
@RequestMapping(value = "/users", produces="application/json;charset=UTF-8")
public String usersList() {

    ArrayList<String> users =  new ArrayList<String>(){{
        add("freewolf");
        add("tom");
        add("jerry");
    }};

    return JSONResult.fillResultString(0, "", users);
}

@RequestMapping(value = "/hello", produces="application/json;charset=UTF-8")
public String hello() {
    ArrayList<String> users =  new ArrayList<String>(){{ add("hello"); }};
    return JSONResult.fillResultString(0, "", users);
}

@RequestMapping(value = "/world", produces="application/json;charset=UTF-8")
public String world() {
    ArrayList<String> users =  new ArrayList<String>(){{ add("world"); }};
    return JSONResult.fillResultString(0, "", users);
}

{
“result”: [
“freewolf”,
“tom”,
“jerry”
],
“message”: “”,
“status”: 0
}
如果你細心,你會發現這裡的JSON返回時,Chrome的格式化外掛好像並沒有識別?這是為什麼呢?我們藉助curl分別看一下我們寫的兩個方法的Header資訊.