您的位置:首页 > 编程语言 > Java开发

SpringMVC+JWT+Swagger UI+RestFul

2017-08-14 11:59 417 查看
前言:
其实很早就想写这篇文章了,因为我觉得这会对很多新手有指引作用,当初自己也是瞎子过河的摸索着过来的。目前后台开发比较流行的MVC框架中使用Spring MVC还是比较多的,当然还有Spring Boot,Spring Boot是基于 Spring4 的条件注册的一套快速开发整合包,说白了就是简化开发流程。大家可以尝试一下,但是这里还是以Spring MVC为例子。
之所以使用jwt(json web token),是因为做后台不同于做web,app因为是长时间的登录至少都是一两个月不操作任然处于登录状态,所以目前国内大多是都是使用token做鉴权而不是使用session。而jwt是一个不错的token技术。restful就不用多说了,现在比较流行的编程风格,多个客户端(安卓,ios, mobile,web)调用同一套后台接口,而这就使得这套后台接口尽量不附带太多业务逻辑,而是面向资源的风格。而swaggerui则是提供一个rest api的可视化接口文档,并且在开发完后,可以很轻松的测试。
当然你可以只整合jwt或者swaggerui,教程都是没问题的。
[b]所有使用到的代码可以在这里查看:https://github.com/minchangchen/springmvc-jwt-swaggerui[/b]1.Spring MVC
Spring MVC框架是有一个MVC框架,通过实现Model-View-Controller模式来很好地将数据、业务与展现进行分离。从这样一个角度来说,Spring MVC和Struts、Struts2非常类似。Spring MVC的设计是围绕DispatcherServlet展开的,DispatcherServlet负责将请求派发到特定的handler。通过可配置的handler mappings、view resolution、locale以及theme resolution来处理请求并且转到对应的视图。这里就不做太多介绍,开始想在维基百科上搜索下spring mvc的,结果发现并没有这个条目,百度是有的。所以说有时候盈利的并不是都不好。
2.JWT(Json Web Tokens)
定义:JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
说明:传统的鉴权机制是基于session-cookies,而随着认证用户的增多,服务端的开销会明显增大,且不适合做app应用验证。因为app是一次登录,退出后不需登录,所以现在主流的鉴权验证都是使用token验证。而jwt是一种不错的基于token的鉴权机制。
基本流程:
用户使用用户名密码来请求服务器
服务器进行验证用户的信息
服务器通过验证发送给用户一个token
客户端存储token,并在每次请求时附送上这个token值
服务端验证token值,并返回数据
推文:http://www.jianshu.com/p/576dbf44b2ae
3.RestFul
定义:网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。
说明:restful风格,就是一种面向资源服务的API设计方式,它不是规范,不是标准,它一种设计模式。以前流行的web service服务都是面向过程,基于RPC协议的SOAP协议,对于现在或者未来,更多的人了解并且深受SOA思想影响,以面向服务为目标,而现在的SOAP虽然支持SOA,但存在很很大的差别,所以,慢慢就流行基于restful风格的web service。说简单一点,就是它纯粹面向资源,面向服务的思想,目前J2EE6的JAX-RS就是这种restful风格实现的新技术。
例子:
获取用户列表 GET:http://project.company.com/api/v1/users
获取单个用户 GET:http://project.company.com/api/v1/users/{uid:.{32}}
创建单个用户 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}}
推文:http://www.ruanyifeng.com/blog/2014/05/restful_api.html4.Swagger UI
定义:Swagger的目标是为REST APIs 定义一个标准的,与语言无关的接口,使人和计算机在看不到源码或者看不到文档或者不能通过网络流量检测的情况下能发现和理解各种服务的功能。当服务通过Swagger定义,消费者就能与远程的服务互动通过少量的实现逻辑。类似于低级编程接口,Swagger去掉了调用服务时的很多猜测。
说明:swagger ui用于管理项目中API接口,属当前最流行的API接口管理工具。是后端开发人员提供给app开发人员的一个查看、测试、的一个可视化,可操作的接口文档,在这里你可以知道需要给都太传入什么参数,使用哪种请求,以及返回的数据等等,这种就省去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是否为需要鉴权的路径,然后去验证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);

@Override
public 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端存也要是这个key
ETokenState 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);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}

@Override
public 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是不拦截的,自己可以配置。关于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资源重定向(仅作为后台使用不提供静态资源) -->
<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 {

@Bean
public Docket api(){
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
//增加一个request的header参数
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端人员调用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 {

@Autowired
UserSO so;

@ApiOperation(value="验证密文,并添加user", notes="密文和amigoInfo")
@RequestMapping( value = {"/login"}, method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody
public String login(HttpServletRequest request, @RequestParam String fields, @PathVariable String useruid) {
// do something
}

/*
*api开头的注解都是swagger的注解
*方法参数上加有@RequestParam的参数会显示在swagger
*所以上述login方法的三个参数只有fields会显示在swagger中
*当然还有一个请求头的参数,存放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}}
创建单个用户  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}}

至此,所有配置应该都已经贴出,代码可在这里查找:https://github.com/minchangchen/springmvc-jwt-swaggerui

若有遗失或错误希望大家提出!过段时间我会写一些与docker有关的教程,这段时间觉得自己对docker的使用还不够全及精,所以暂时不写!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息