JWT(json web token)。
1,jwt(全称json web token)。主要是用来生成token的,token是一串加密过的字符串。
流程是这样的
- 用户使用用户名密码请求服务器
- 服务器进行验证用户信息,并生成一个token存在数据库中
- 服务器通过验证并把token发送给用户
- 客户端存储token,一般存在local Storage。并在每次请求时附加这个token值,可以以参数形式加在ajax的请求头或者请求体中
- 服务器验证token,token验证与数据库中存的token比较,验证通过,则做业务逻辑处理并返回数据
好处是这样的
- 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输
- 更适用于移动应用: 如果客户端是一个iOS, Android时,Cookie是不被支持的,这时采用Token认证机制就会简单得多。
- 上手简单:贼简单的那种。下面细说。
- 你猜
2,实现步骤(看了下别的博客的介绍,把简单的步骤搞的很复杂,所以自己重新捋了一遍)
- pom文件中添加依赖
<!-- 添加依赖JwtPermission --> <dependency> <groupId>com.github.whvcse.JwtPermission</groupId> <artifactId>jwtp-spring-boot-starter</artifactId> <version>2.0.2</version> </dependency>
- 添加相应的实体、数据库建对应的表。各个表的关联关系应该从表名的字面意思上就可以了解,实在不了解的话可以看下面的建表语句中有字段备注介绍(前五张sys相关的表需要创建相应的实体,token相关的表只需要建表就好了不需要创建实体)
1,系统用户表:sys_user
2,系统角色表:sys_role
3,系统权限表:sys_authorities
4,用户角色关联表:sys_user_role
5,角色权限关联表:sys_role_authorities
6,根据jar包中的代码建oauth_token表和oauth_token_key表。
https://download.csdn.net/download/nienianzhi1744/11878539(具体见建表sql和表关系说明都在这个链接里)
3. 登录的时候校验用户名和密码,通过后创建token存到数据库并返回给客户端。
// 几个公司私有的接口(userJpaService,userRoleService,authoritiesService)没有导入,这几个接口主要写的是根据id查询实体的方法
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.wf.jwtp.provider.Token;
import org.wf.jwtp.provider.TokenStore;
@Api(value = "其他配置——用户登录相关接口", tags = "main")//swagger注解,可以不加
@Controller
public class MainController extends BaseController {
@Autowired
private UserJpaService userJpaService;
@Autowired
private UserRoleJpaService userRoleService;
@Autowired
private AuthoritiesJpaService authoritiesService;
@Autowired
private TokenStore tokenStore;
@ResponseBody
@ApiOperation(value = "用户登录")//ApiOperation和ApiImplicitParams是swgger注解,便于接口API交互,可以不加
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "账号", required = true, dataType = "String", paramType = "form"),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String", paramType = "form")
})
@PostMapping("${api.version}/login")
@Log(describe = "用户登录",operationType= OperationType.LOGON)//此处为自定义注解,用于记录用户操作,可以不加
public Map login(String username, String password) {
Map returnMap=new HashMap<>();
/*根据用户名获取实体*/
User user = userJpaService.getByUsername(username);
/*若数据库中无该用户的数据*/
if(user==null){
returnMap.put("fail","登录失败");
}
/*若密码对不上,数据库中的密码应该是加密的,此处仅为演示不做加密处理*/
else if(!password.equals(user.getPassword())){
returnMap.put("fail","密码输入有误");
}else{
/*根据用户id获取角色的id集合*/
String[] roles = arrayToString(userRoleService.getRoleIds(user.getUserId()));
/*根据用户id获取权限id集合*/
String[] permissions = listToArray(authoritiesService.listByUserId(user.getUserId()));
/*调用TokenStore的createNewToken创建token*/
Token token = tokenStore.createNewToken(String.valueOf(user.getUserId()), permissions, roles);
returnMap.put("success","登录成功");
returnMap.put("access_token",token.getAccessToken());
}
return returnMap;
}
private String[] listToArray(List<String> list) {
String[] strs = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
strs[i] = list.get(i);
}
return strs;
}
private String[] arrayToString(Object[] objs) {
String[] strs = new String[objs.length];
for (int i = 0; i < objs.length; i++) {
strs[i] = String.valueOf(objs[i]);
}
return strs;
}
}
客户端接收到返回的map中的token后通过
localStorage.setItem("key","value")将map中的token值存到localstorage。
4,最后一步,这一步就简单了。
登录后在后续的ajax的业务请求中,把localstorage中的token值带上,在后台与数据库中的值校验。这个方法就比较多啦。
先说下策(不建议使用),直接将token以参数形式放到后台,后台接收并校验。
再说上策(建议使用):
前端:重新封装ajax请求。将token的值放到请求头中。这样每次使用ajax的时候就不用添加token了,直接调用这个封装好的ajax直接使用。
var token=localStorage.getItem("key")
$.ajax({
type: "post",
url:"xxx",
contentType: "application/json;charset=utf-8",
data :JSON.stringify({"tbId": 1}),
dataType: "json",
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("Authorization", "Bearer "+token);
},
success: function (data) {
alert(data);
},error:function(error){
console.log(error);
}
)
后端:配置swagger注解,swagger的配置中将参数中的请求头作为必传项。在被请求的接口上面添加swaager注解,校验请求头中的token。
1,添加pom依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
2,swagger配置
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Parameter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(headerInfo());//************把消息头添加;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API文档")
.description("后台管理系统")
.termsOfServiceUrl("")
.version("1.0")
.build();
}
private List<Parameter> headerInfo(){
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
ticketPar.name("Authorization").description("access_token")//name表示名称,description表示描述
.modelRef(new ModelRef("string")).parameterType("header")
.required(true).defaultValue("Bearer ").build();//required表示是否必填,defaultvalue表示默认值
pars.add(ticketPar.build());//添加完此处一定要把下边的带***的也加上否则不生效
return pars;
}
}
3,在接口上添加swagger注解,如图
这样,添加了swagger注解的接口在请求的时候就会自动校验请求中请求头中的 token。