前后端分离
一般来说,我们用SpringSecurity默认的话是前后端整在一起的,比如thymeleaf或者Freemarker,SpringSecurity还自带login登录页,还让你配置登出页,错误页。
但是现在前后端分离才是正道,前后端分离的话,那就需要将返回的页面换成Json格式交给前端处理了
SpringSecurity默认的是采用Session来判断请求的用户是否登录的,但是不方便分布式的扩展,虽然SpringSecurity也支持采用SpringSession来管理分布式下的用户状态,不过现在分布式的还是无状态的Jwt比较主流。 所以怎么让SpringSecurity变成前后端分离,可以采用Jwt来做认证
什么是jwt
Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519).该token被设计为紧凑且==安全==的,特别适用于==分布式站点的单点登录(SSO)场景==。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
官网: JSON Web Token Introduction - jwt.io
-
- jwt的结构
Header
Header 部分是一个JSON对象,描述JWT的元数据,通常是下面的样子。
{
"alg": "HS256",
"typ": "JWT"
}
上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256 (写成 HS256) ;typ属性表示这个令牌(token)的类型(type), JWT令牌统一写为JWT。
最后,将上面的JSON对象使用Base64URL算法转成字符串。
Payload(荷载)
Payload 部分也是一个JSON对象,==用来存放实际需要传递的数据==。JWT规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (lssued At):签发时间
jti (JWT ID):编号
除了官方字段,==你还可以在这个部分定义私有字段==,下面就是一个例子。
{
"sub": "1234567890",
"name" : "John Doe",
“userid”:2
"admin": true
}
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把==秘密信息==放在这个部分。这个JSON 对象也要使用Base64URL 算法转成字符串。
Signature
Signature部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个==密钥(secret)==。这个密钥只有==服务器才知道==,不能泄露给用户。然后,使用Header里面指定的==签名算法(默认是 HMAC SHA256)==,按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + ".”"+base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
项目添加hutool依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
认证成功
实现AuthenticationSuccessHandler接口,当登录成功后,该处理类的方法被调用
package com.example.securitydemo.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 认证成功
* @return
*/
public AuthenticationSuccessHandler getSuccessHandler(){
return (httpServletRequest, response, authentication) -> {
User user=(User) authentication.getPrincipal();
//Collection<GrantedAuthority> authorities = user.getAuthorities();
List<String> collect = user.getAuthorities().stream().map(grantedAuthority -> grantedAuthority.getAuthority()).collect(Collectors.toList());
// 生成token
String username = user.getUsername();
Map map = new HashMap();
map.put("username",username);
map.put("allresource",collect);
Calendar calendar =Calendar.getInstance();
calendar.add(Calendar.HOUR,2);
// 设置过期的时间
Date expireTime = calendar.getTime();
System.out.println("过期的时间是:"+expireTime);
// 设置生效的时间
Date time = calendar.getTime();
System.out.println("生效的时间是:"+time);
map.put(JWTPayload.EXPIRES_AT,expireTime);// 过期的时间
map.put(JWTPayload.ISSUED_AT,time);// 签发的时间
map.put(JWTPayload.NOT_BEFORE,time);// 生效的时间
String token = JWTUtil.createToken(map, "yyl".getBytes());
Result result = new Result(token);
printJsonData(response,result);
};
}
public void printJsonData(Result result,HttpServletResponse response) throws IOException {
// 以json的形式 传递除去
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(result);
// 响应到前端
response.setContentType("application/json;charset=utf8");
PrintWriter writer = response.getWriter();
writer.write(s);
writer.flush();
writer.close();
}
认证失败
实现AuthenticationFailureHandler接口,当登录失败后,该处理类的方法被调用
package com.example.securitydemo.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.Authen