SpringBoot鉴权之——JWT鉴权
第⼀:什么是JWT鉴权
1. JWT即JSON Web Tokens,是为了在⽹络应⽤环境间传递声明⽽执⾏的⼀种基于JSON的开放标准(),他可以⽤来安全的传递信息,因为传递的信息是经过加密算法加密过得。
2.JWT常⽤的加密算法有:HMAC算法或者是RSA的公私秘钥对进⾏签名,也可以使⽤公钥/私钥的⾮对称算法
3.JWT的使⽤场景主要包括:
1) 认证授权,特别适⽤于分布式站点的单点登录(SSO)场景,只要⽤户开放的登录⼊⼝登录过⼀次系统,就会返回⼀个token,之后的请求都需要包含token。
        2)交换信息,通过使⽤密钥对来安全的传送信息,可以知道发送者是谁、放置消息是否被篡改,⼀般被⽤来在⾝份提供者和服务提供者之间传递被认证的⽤户⾝份信息,以便于从资源服务器获取资源,也可以增加⼀些额外的其它业务逻辑所必须的声明信息,例如:设备信息,版本号等,该token也可直接被⽤于认证,也可被加密。
第⼆:JWT构成
JSON Web Tokens(JWT)有三部分构成,⽤英⽂句点分割(.) ,⼀般看起来例如:
分为:
Header  头信息
Payload  荷载信息,实际数据
Signature  由头信息+荷载信息+密钥组合之后进⾏加密得到
  1) Header 头信息通常包含两部分,type:代表token的类型,这⾥使⽤的是JWT类型。 alg:代表使⽤的算法,例如HMAC SHA256或RSA.
{
"alg": "HS256",
"typ": "JWT"
}// 这会被经过base64Url编码形成第⼀部分
2)Payload ⼀个token的第⼆部分是荷载信息,它包含⼀些声明Claim(实体的描述,例:⽤户信息和其他的⼀些元数据)
声明分三类:
    1)Reserved Claims,这是⼀套预定义的声明,并不是必须的,这是⼀套易于使⽤、操作性强的声明。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等
    2)Plubic Claims,
          3)Private Claims,交换信息的双⽅⾃定义的声明
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}//同样经过Base64Url编码后形成第⼆部分
    3) signature  使⽤header中指定的算法将编码后的header、编码后的payload、⼀个secret进⾏加密
  例如使⽤的是HMAC SHA256算法,⼤致流程类似于: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
  这个signature字段被⽤来确认JWT信息的发送者是谁,并保证信息没有被修改
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
) secret base64 encoded
第三步:JWT认证流程
上图是官⽅提供的⼀个认证流程图,我们可以看到它的授权流程是:
1.客户端通过post请求请求服务端登录认证接⼝
2.服务端⽤秘密创建JWT
3.服务端将JWT返回浏览器
4.客户端在授权报头上发送JWT
5.服务端检查JWT签名从JWT获取⽤户信息
6.服务端向客户端发送响应
通常我们所看到的认证流程,只能看到第⼀步和第六步,如果使⽤调试模式或者⽤抓包⼯具抓取就可以看到完整流程。第四步:jwt使⽤
源码地址:
github: github/GitHubZhangCom/spring-security-oauth-example/
码云:gitee/region/spring-security-oauth-example/tree/master/spring-jwt
1)、引⼊相关jar:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!-- 使⽤lombok优雅的编码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2)、jwt相关编程
JwtUtil:jwt⼯具类
import org.springframework.util.StringUtils;
/**
* jwt⼯具类
* @author zyl
*
*/
public class JwtUtils {
private static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";
/
**
* 获取原始令牌
* remove 'Bearer ' string
*
* @param authorizationHeader
* @return
*/
public static String getRawToken(String authorizationHeader) {
return authorizationHeader.substring(AUTHORIZATION_HEADER_PREFIX.length());
}
/**
* 获取令牌头
* @param rawToken
* @return
*/
public static String getTokenHeader(String rawToken) {
return AUTHORIZATION_HEADER_PREFIX + rawToken;
}
/**
* 验证授权请求头
* @param authorizationHeader
* @return
*/
public static boolean validate(String authorizationHeader) {
return StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_HEADER_PREFIX);
}
/**
* 获取授权头前缀
* @return
*/
public static String getAuthorizationHeaderPrefix() {
return AUTHORIZATION_HEADER_PREFIX;
}
}
JwtAuthenticationFilter
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.ontext.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.jwt.server.util.JwtUtils;
import io.jsonwebtoken.Jwts;
/**
* ⾃定义JWT认证过滤器
* 该类继承⾃BasicAuthenticationFilter,在doFilterInternal⽅法中,
* 从http头的Authorization 项读取token数据,然后⽤Jwts包提供的⽅法校验token的合法性。
* 如果校验通过,就认为这是⼀个取得授权的合法请求
* @author zyl
*
*/
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {        String header = Header("Authorization");
if (header == null || !header.AuthorizationHeaderPrefix())) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(header);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
String user = Jwts.parser()
.setSigningKey("PrivateSecret")
.AuthorizationHeaderPrefix(), ""))
.getBody()
.getSubject();
if (null != user) {
spring framework是什么系统
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
}
JwtLoginFilter
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.Authentication;
import org.AuthenticationException;
import org.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jwt.server.domain.UserInfo;
import com.jwt.server.util.JwtUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
* 验证⽤户名密码正确后,⽣成⼀个token,并将token返回给客户端
* 该类继承⾃UsernamePasswordAuthenticationFilter,重写了其中的2个⽅法 attemptAuthentication
* :接收并解析⽤户凭证。 successfulAuthentication :⽤户成功登录后,这个⽅法会被调⽤,我们在这个⽅法⾥⽣成token。 *
*/
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JwtLoginFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UserInfo user = new ObjectMapper().InputStream(), UserInfo.class);
return authenticationManager.authenticate(
new Username(), Password(), new ArrayList<>()));        } catch (IOException e) {
throw new RuntimeException(e);
}
}
// ⽤户成功登录后,这个⽅法会被调⽤,我们在这个⽅法⾥⽣成token
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,            Authentication authResult) throws IOException, ServletException {
String token = Jwts.builder().setSubject(((User) Principal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(SignatureAlgorithm.HS512, "PrivateSecret")pact();
response.addHeader("Authorization", TokenHeader(token));
}
}
SecurityConfiguration  config配置
import t.annotation.Configuration;
import org.fig.annotation.web.builders.HttpSecurity;
import org.fig.annotation.web.builders.WebSecurity;
import org.fig.figuration.WebSecurityConfigurerAdapter;
import com.jwt.server.filter.JwtAuthenticationFilter;
import com.jwt.server.filter.JwtLoginFilter;
/**
* 通过SpringSecurity的配置,将JWTLoginFilter,JWTAuthenticationFilter组合在⼀起
*
* @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 在springboot1.5.8的时候该注解是可以⽤的具体看源码
* @author zyl
*
*/
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
}
@Override
protected void configure(HttpSecurity http) throws Exception {
/
/⾃定义默认
.anyRequest().authenticated()
.and()
.requestMatchers().antMatchers("/user/login","/login","/oauth/authorize")
.and()
.addFilter(new JwtLoginFilter(authenticationManager()))//登录过滤器
.addFilter(new JwtAuthenticationFilter(authenticationManager()));//⾃定义过滤器
}
}
UserInfo 认证⽤户
import lombok.Data;
/**
* 认证⽤户
* @author zyl
*
*/
@Data
public class UserInfo {
private String id;
private String username;
private String password;
public UserInfo() {
this.setId("testId");
this.setUsername("testUsername");
this.setPassword("testPassword");
}
}
UserDetailServiceImpl:核⼼认证⽤户service类
import static java.ptyList;
import org.userdetails.User;
import org.userdetails.UserDetails;
import org.userdetails.UserDetailsService;
import org.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.jwt.server.domain.UserInfo;
/**
*
* @author zyl
*
*/
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws  UsernameNotFoundException {        UserInfo user = new UserInfo();
return new Username(), Password(), emptyList());
}
}
第五步:测试
请求登录:localhost:8085/login
这⾥测试登录的是请⽤post⽅式,因为默认源码⾥边只⽀持post⽅式

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。