javascript
SpringMVC+JWT+Swagger UI+RestFul
前言:
其實很早就想寫這篇文章了,因為我覺得這會對很多新手有指引作用,當初自己也是瞎子過河的摸索著過來的。目前后臺開發(fā)比較流行的MVC框架中使用Spring MVC還是比較多的,當然還有Spring Boot,Spring Boot是基于 Spring4 的條件注冊的一套快速開發(fā)整合包,說白了就是簡化開發(fā)流程。大家可以嘗試一下,但是這里還是以Spring MVC為例子。
之所以使用jwt(json web token),是因為做后臺不同于做web,app因為是長時間的登錄至少都是一兩個月不操作任然處于登錄狀態(tài),所以目前國內(nèi)大多是都是使用token做鑒權(quán)而不是使用session。而jwt是一個不錯的token技術(shù)。restful就不用多說了,現(xiàn)在比較流行的編程風格,多個客戶端(安卓,ios, mobile,web)調(diào)用同一套后臺接口,而這就使得這套后臺接口盡量不附帶太多業(yè)務(wù)邏輯,而是面向資源的風格。而swaggerui則是提供一個rest api的可視化接口文檔,并且在開發(fā)完后,可以很輕松的測試。
當然你可以只整合jwt或者swaggerui,教程都是沒問題的。
所有使用到的代碼可以在這里查看:https://github.com/minchangchen/springmvc-jwt-swaggerui
1.Spring MVC
Spring MVC框架是有一個MVC框架,通過實現(xiàn)Model-View-Controller模式來很好地將數(shù)據(jù)、業(yè)務(wù)與展現(xiàn)進行分離。從這樣一個角度來說,Spring MVC和Struts、Struts2非常類似。Spring MVC的設(shè)計是圍繞DispatcherServlet展開的,DispatcherServlet負責將請求派發(fā)到特定的handler。通過可配置的handler mappings、view resolution、locale以及theme resolution來處理請求并且轉(zhuǎn)到對應(yīng)的視圖。這里就不做太多介紹,開始想在維基百科上搜索下spring mvc的,結(jié)果發(fā)現(xiàn)并沒有這個條目,百度是有的。所以說有時候盈利的并不是都不好。
2.JWT(Json Web Tokens)
定義:JWT是一種用于雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規(guī)范。JWT作為一個開放的標準( RFC 7519 ),定義了一種簡潔的,自包含的方法用于通信雙方之間以Json對象的形式安全的傳遞信息。因為數(shù)字簽名的存在,這些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。
說明:傳統(tǒng)的鑒權(quán)機制是基于session-cookies,而隨著認證用戶的增多,服務(wù)端的開銷會明顯增大,且不適合做app應(yīng)用驗證。因為app是一次登錄,退出后不需登錄,所以現(xiàn)在主流的鑒權(quán)驗證都是使用token驗證。而jwt是一種不錯的基于token的鑒權(quán)機制。
基本流程:
用戶使用用戶名密碼來請求服務(wù)器
服務(wù)器進行驗證用戶的信息
服務(wù)器通過驗證發(fā)送給用戶一個token
客戶端存儲token,并在每次請求時附送上這個token值
服務(wù)端驗證token值,并返回數(shù)據(jù)
推文:http://www.jianshu.com/p/576dbf44b2ae
3.RestFul
定義:網(wǎng)絡(luò)應(yīng)用程序,分為前端和后端兩個部分。當前的發(fā)展趨勢,就是前端設(shè)備層出不窮(手機、平板、桌面電腦、其他專用設(shè)備......)。因此,必須有一種統(tǒng)一的機制,方便不同的前端設(shè)備與后端進行通信。這導致API構(gòu)架的流行,甚至出現(xiàn)"API First"的設(shè)計思想。RESTful API是目前比較成熟的一套互聯(lián)網(wǎng)應(yīng)用程序的API設(shè)計理論。
說明:restful風格,就是一種面向資源服務(wù)的API設(shè)計方式,它不是規(guī)范,不是標準,它一種設(shè)計模式。以前流行的web service服務(wù)都是面向過程,基于RPC協(xié)議的SOAP協(xié)議,對于現(xiàn)在或者未來,更多的人了解并且深受SOA思想影響,以面向服務(wù)為目標,而現(xiàn)在的SOAP雖然支持SOA,但存在很很大的差別,所以,慢慢就流行基于restful風格的web service。說簡單一點,就是它純粹面向資源,面向服務(wù)的思想,目前J2EE6的JAX-RS就是這種restful風格實現(xiàn)的新技術(shù)。
例子:
獲取用戶列表 GET:http://project.company.com/api/v1/users
獲取單個用戶 GET:http://project.company.com/api/v1/users/{uid:.{32}}
創(chuàng)建單個用戶 POST:http://project.company.com/api/v1/users/{uid:.{32}}
完全替換用戶 PUT:http://project.company.com/api/v1/users/{uid:.{32}}
局部更新用戶 PATCH:http://project.company.com/api/v1/users/{uid:.{32}}
刪除單個用戶 DELETE:http://project.company.com/api/v1/users/{uid:.{32}}
4.Swagger UI
定義:Swagger的目標是為REST APIs 定義一個標準的,與語言無關(guān)的接口,使人和計算機在看不到源碼或者看不到文檔或者不能通過網(wǎng)絡(luò)流量檢測的情況下能發(fā)現(xiàn)和理解各種服務(wù)的功能。當服務(wù)通過Swagger定義,消費者就能與遠程的服務(wù)互動通過少量的實現(xiàn)邏輯。類似于低級編程接口,Swagger去掉了調(diào)用服務(wù)時的很多猜測。
說明:swagger ui用于管理項目中API接口,屬當前最流行的API接口管理工具。是后端開發(fā)人員提供給app開發(fā)人員的一個查看、測試、的一個可視化,可操作的接口文檔,在這里你可以知道需要給都太傳入什么參數(shù),使用哪種請求,以及返回的數(shù)據(jù)等等,這種就省去app端人員寫測試接口。
圖示:
代碼:
1.maven
<!-- Spring --> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.4.RELEASE</version><exclusions><!-- Exclude Commons Logging in favor of SLF4j --><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>4.3.4.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.4.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>4.3.4.RELEASE</version> </dependency><!-- springfox-swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.5.0</version> </dependency><!-- Jwt --> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.7.0</version> </dependency>2.JWT
這里我不再解釋下jwt了,looking this :http://www.jianshu.com/p/576dbf44b2ae 一定要看哦!
我先說下jwt使用流程:
a:用戶登錄server
b:server驗證通過,生成token并返回
c:app端接收到token存起來,下去請求放在reuqest的header里面
d:server接收到app的請求,在攔截器中判斷url是否為需要鑒權(quán)的路徑,然后去驗證token
e:當token過期時,且沒有超過刷新期。自動添加一個新的token返回給app
攔截器:
public abstract class BaseWebInterceptor extends HandlerInterceptorAdapter {public static final String RedirectPrefix = "redirect:";private static Logger log = LoggerFactory.getLogger(BaseWebInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (isNeedJwtUrl(request.getRequestURI())) { //判斷這個url是否需要jwt驗證 ,比如login就不需要String token = request.getHeader("x-access-token"); //從request中獲取token,key是自定義的,當然app端存也要是這個keyETokenState state = JwtUtil.validateJWT(token); //驗證token,ETokenState這是我自定義的一個類,JwtUtil這也是自己寫的一個類switch (state) { case invalid: //驗證錯誤log.info(String.format("URL:%s need login, but the token is null or invalid... ", request.getRequestURL().toString()));response.setStatus(401);return false;case expired: //token過期if (refreshTokenHandler(request, response, token)) {break;} else {log.info(String.format("URL:%s need login, but the token is expired... ", request.getRequestURL().toString()));response.setStatus(403);return false;}case valid: //有效的break;default:break;}}return super.preHandle(request, response, handler);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {super.afterCompletion(request, response, handler, ex);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {super.postHandle(request, response, handler, modelAndView);}/*** 刷新token,子類重載* * @author chenmc* @date 2017年5月9日 下午3:41:05* @param token* @return*/protected boolean refreshTokenHandler(HttpServletRequest request, HttpServletResponse response, String token) {return false;}/*** 需要登錄的uri* * @param requestURI* @return*/private boolean isNeedJwtUrl(String requestURI) {return MappingConf.isNeedJwtUrls(requestURI);}}這里需要說明的是,一般的login和register的url是不攔截的,自己可以配置。關(guān)于jwt生成和驗證的代碼,請點擊這里https://github.com/minchangchen/springmvc-jwt-swaggerui
2.Swagger UI
配置文件:
1,springmvc.xml
<!-- swagger2 --><context:component-scan base-package="com.gionee.swagger.conf"/><bean class="com.company.swagger.conf.SwaggerConfig"/><!-- Swagger資源重定向(僅作為后臺使用不提供靜態(tài)資源) --><mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/><mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/>2,SwaggerConfig.java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.web.servlet.config.annotation.EnableWebMvc;import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.ParameterBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.schema.ModelRef; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Parameter; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;//@Configuration @EnableSwagger2 @EnableWebMvc @ComponentScan("com.company.web") public class SwaggerConfig {@Beanpublic Docket api(){ParameterBuilder tokenPar = new ParameterBuilder();List<Parameter> pars = new ArrayList<Parameter>();//增加一個request的header參數(shù)tokenPar.name("x-access-token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build();pars.add(tokenPar.build());return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.regex("/api/.*"))//對所有請求中包含api的url攔截.build().globalOperationParameters(pars).apiInfo(apiInfo());}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("后臺接口文檔與測試").description("這是一個給app端人員調(diào)用server端接口的測試文檔與平臺").version("1.0.0").termsOfServiceUrl("http://terms-of-services.url")//.license("LICENSE")//.licenseUrl("http://url-to-license.com").build();}}3,Controller
@Api(value="登錄接口") @Controller @RequestMapping("/api/v1") public class LoginInterface extends BaseController {@AutowiredUserSO so;@ApiOperation(value="驗證密文,并添加user", notes="密文和amigoInfo")@RequestMapping( value = {"/login"}, method = RequestMethod.POST, produces = "application/json;charset=UTF-8")@ResponseBodypublic String login(HttpServletRequest request, @RequestParam String fields, @PathVariable String useruid) {// do something}/**api開頭的注解都是swagger的注解*方法參數(shù)上加有@RequestParam的參數(shù)會顯示在swagger*所以上述login方法的三個參數(shù)只有fields會顯示在swagger中*當然還有一個請求頭的參數(shù),存放token的那是在SaggerConfig.java中配置的*/ }4,放開swagger的url不攔截
當你配置了jwt,因為jwt對url進行攔截,這里我們不能對swagger的URL進行攔截,這樣頁面才能正常顯示。只整合swaggerui的可以忽略。
noNeedJwtUrls=.*swagger.*|.*docs.*|.*test.*|.*/index.*|.*/register.*|.*/login.*至此,swaggerui已配置完成,請求訪問http://localhost:8080/proj-name/swagger-ui.html即可得到以下頁面
3.RestFul API
請參照以下這種方式來定義接口
獲取用戶列表 GET:http://proj.company.com/api/v1/users
獲取單個用戶 GET:http://proj.company.com/api/v1/users/{uid:.{32}}
創(chuàng)建單個用戶? POST:http://proj.company.com/api/v1/users/{uid:.{32}}
完全替換用戶 PUT:http://proj.company.com/api/v1/users/{uid:.{32}}
局部更新用戶 PATCH:http://proj.company.com/api/v1/users/{uid:.{32}}
刪除單個用戶 DELETE:http://proj.company.com/api/v1/users/{uid:.{32}}
至此,所有配置應(yīng)該都已經(jīng)貼出,代碼可在這里查找:https://github.com/minchangchen/springmvc-jwt-swaggerui。
若有遺失或錯誤希望大家提出!過段時間我會寫一些與docker有關(guān)的教程,這段時間覺得自己對docker的使用還不夠全及精,所以暫時不寫!
總結(jié)
以上是生活随笔為你收集整理的SpringMVC+JWT+Swagger UI+RestFul的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nfs服务启动失败:Failed to
- 下一篇: Spring及SpringBoot @A