需求
需求背景:项目前后端分离,前端页面在一个服务器上,后台接口在另一个服务器,也就是非同源跨域请求。页面通过调用接口获取数据并对数据库进行crud的操作,并需要在用户登录的条件下进行操作,这里我们利用JWT(Json Web Token)解决跨域获取登录用户信息的问题。
思路
1.首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTPPOST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
2.后端核对用户名和密码成功后,将用户的id等其他信息作为JWT
Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。
3.后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
4.前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
5.后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
6.验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。
用法
1.前端服务器页面点击登录填写登录信息
这里页面登录通过ajax jsonp 实现跨域请求后台服务器登录接口,下面代码后台登录代码,登录成功生成token。
@RequestMapping(value = "login",produces = "text/script;charset=UTF-8")
@ResponseBody
public String login(HttpServletRequest request, HttpServletResponse response,String callback,String userName,String passWord) {
Map<String,Object> map =MapUtils.newHashMap();
if(StringUtils.isNotBlank(userName)&&StringUtils.isNotBlank(passWord)){
User user = UserUtils.getByLoginCode(userName);
if(user!=null&&StringUtils.isNotBlank(user.getUserCode())){
if(user.getPassword().equals(passWord)){
//生成jwt token
String token = JwtUtils.geneJsonWebToken(user);
map.put("result","true");
map.put("message","登录成功!");
map.put("token",token);
org.json.JSONObject object = new org.json.JSONObject(map);
return callback + "(" + object.toString() + ")";
}else{
map.put("result","false");
map.put("message","登录失败,密码有误!");
org.json.JSONObject object = new org.json.JSONObject(map);
return callback + "(" + object.toString() + ")";
}
}else{
map.put("result","false");
map.put("message","登录失败,用户不存在!");
org.json.JSONObject object = new org.json.JSONObject(map);
return callback + "(" + object.toString() + ")";
}
}else{
map.put("result","false");
map.put("message","登陆失败,用户名或密码不能为空!");
org.json.JSONObject object = new org.json.JSONObject(map);
return callback + "(" + object.toString() + ")";
}
}
下面是JwtUtils代码
package com.jeesite.common.jwt;
import com.jeesite.modules.sys.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
/**
* jwt工具类
*/
public class JwtUtils {
public static final String SUBJECT = "xdclass";
//public static final long EXPIRE = 1000*60*60*24*7; //过期时间,毫秒,一周
public static final long EXPIRE = 1000*60*10; //过期时间,毫秒,一周
//秘钥
public static final String APPSECRET = "xd666";
/**
* 生成jwt
* @param user
* @return
*/
public static String geneJsonWebToken(User user){
if(user == null || user.getId() == null || user.getUserCode() == null
|| user.getPassword()==null){
return null;
}
String token = Jwts.builder().setSubject(SUBJECT)
.claim("id",user.getId())
.claim("userName",user.getUserCode())
.claim("passWord",user.getPassword())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis()+EXPIRE))
.signWith(SignatureAlgorithm.HS256,APPSECRET).compact();
return token;
}
/**
* 校验token
* @param token
* @return
*/
public static Claims checkJWT(String token ){
try{
final Claims claims = Jwts.parser().setSigningKey(APPSECRET).
parseClaimsJws(token).getBody();
return claims;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
登录校验成功后,把token存在map中连着登录信息一起返回给前台页面,页面获取到token后使用locationstorage把token存到浏览器中(locationstorage可当做大型号的cookie使用,想了解可以自己搜一下)
<script>
function submit() {
var userName = $("#userName").val();
var passWord = $("#passWord").val();
$.ajax({
type:"post",
url:"http://192.168.11.5:8081/js/a/pm/pmAuctionProcess/login",
data:{'userName': userName,'passWord':passWord},
dataType:"jsonp",
crossDomain:true,
jsonpCallback:"jsonpCallbackFun",
jsonp:"callback",
success:function(data){
if(data!=null&&data['result']=="true"){
$.MsgBox.Alert("消息", data['message']);
console.log(data);
if(!window.localStorage){
alert("浏览器支持localstorage");
}else{
var storage=window.localStorage;
//写入a字段
storage["token"]=data['token'];
console.log(storage.token);
}
}
},
error: function() {
}
});
}
</script>
这时已经存到浏览器的locationstorage中了
然后页面通过jsonp请求获取后台数据时,在data参数中加上这个token传到后台进行校验
@RequestMapping(value = "getData",produces = "text/script;charset=UTF-8")
@ResponseBody
public String getData(String goodsCode,String token, HttpServletRequest request, HttpServletResponse response,String callback) {
//解析token获取用户信息
User user = getUserByToken(token);
.........
}
/**
* 解析token获取用户
* @param token
* @return
*/
public User getUserByToken(String token){
Claims claims= JwtUtils.checkJWT(token);
System.out.println(claims);
User user = new User();
if(claims!=null){
user = UserUtils.get(claims.get("id").toString());
}
return user;
}
每次访问后台接口都要先通过传来的token进行身份校验再进行访问。退出登录后,页面将locationstorage 清理掉即可!