话不多说,直接上代码
引入依赖如下的依赖,springboot不造怎么构建,请先把springboot玩熟了在回来看吧。
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<!--整合shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
集成JWT
配置类与工具类写在一起,有些人喜欢分开写,看个人呗。
package com.emmmya.harin.config.security.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* JWT的token,区分大小写
*/
@Component
public class JwtConfig {
@Value("${config.jwt.secret}")
private String secret;
@Value("${config.jwt.expire:3600}")
private long expire;
@Value("${config.jwt.header}")
private String header;
/**
* 生成token
* @param subject
* @return
*/
public String createToken (String subject){
//System.out.println(split);
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + expire * 1000);//过期时间
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(subject)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 获取token中注册信息
* @param token
* @return
*/
public Claims getTokenClaim (String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 验证token是否过期失效
* @param expirationTime
* @return true表示过期,false表示没过期。
*/
public boolean isTokenExpired (Date expirationTime) {
return expirationTime.before(new Date());
}
/**
* 获取token失效时间
* @param token
* @return
*/
public Date getExpirationDateFromToken(String token) {
return getTokenClaim(token).getExpiration();
}
/**
* 获取用户名从token中
*/
public String getUsernameFromToken(String token) {
return getTokenClaim(token).getSubject();
}
/**
* 获取jwt发布时间
*/
public Date getIssuedAtDateFromToken(String token) {
return getTokenClaim(token).getIssuedAt();
}
// --------------------- getter & setter ---------------------
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
这类里面有依赖注入,我也贴出来给大家看看,secret是我加密过的,不直接展示出来(加密技术是:jasypt,小伙子们可以了解了解)
jwt:
# 加密密钥
secret: ENC(3/pMc6HNFuihXbmxwbCgcMyxuiPT8Z9T4u7I9feygcRRXDKkDQNr4yUGpkTP4UoubwLtrY5sW40PS0qXzYk1PQ==)
# token有效时长(秒)
expire: 3600
# header 名称
header: accept-token
这个对这三个参数做解释:
secret:JWT生成token的加密的密钥。
expire:生token的有时长
header:前端保存token的key名
那么,token的一个整合就弄好了。可以先写个接口来玩一玩了。
@Slf4j
@RestController
@Api("使用shiro+jwt来控制登录")
public class TokenController {
//注入生成token的工具类
@Autowired
JwtConfig jwtConfig;
/**
* 用户登录接口
* @return
*/
@RequestMapping (value = "/user/login")
public Result<Object> login(@ReuqestBody User user){
String token = JwtConfig.createToken(username);
return ResultUtils.success(token);
}
{
code:200,
message:"success",
result:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJIYXJpbiIsImlhdCI6MTU5MDEzODM0OCwiZXhwIjoxNTkwMTQxOTQ4fQ.-FJvZn6P-aOxJFyUad_nLds1zi50ib_cTT8ZEx3227c5MuP53_DQcs0H1sWwFEiFFEXUGGLnC4SDcXXh-I1k9A"
}
result里的数据就是token。这个格式的数据如果不清楚的,请先了解 什么是jwt,戳下面⬇⬇⬇⬇
什么是jwt??
前面我只是说集成进来springboot中,但没有说着运用到项目里了。
想想jwt的作用,那就是作为登录令牌使用。那就是在每次请求中,都需要浏览器传这个参数给后台,辨认该请求时登录过的了(也可以用于单点登录)。
那么,后台需要前端传过来token并校验token有效性。既然每个请求都需要做校验。从学过的技术与结合当前结合的技术来想,可以考虑,过滤器,拦截器两种方式。
把jwt结合到项目中
拦截器写法:
package com.emmmya.harin.interceptor;
import com.emmmya.harin.common.constant.RedisConstant;
import com.emmmya.harin.common.utils.FileNameUtils;
import com.emmmya.harin.common.utils.HResourcesUtils;
import com.emmmya.harin.common.utils.RedisUtil;
import com.emmmya.harin.common.utils.ShiroUtils;
import com.emmmya.harin.config.security.jwt.JwtConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.thymeleaf.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 因为一开始没有redis。所以这个过期时间配置在toekn里面。
*/
@Component
public class JwtTokenInterceptor extends HandlerInterceptorAdapter {
@Value("${jwt.header}")
private String header;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws SignatureException, IOException, ServletException {
/** 这两个地址,直接放行 */
String uri = request.getRequestURI();
System.out.println("JwtTokenInterceptor:"+uri);
if ("/login".equals(uri) || "/logout".equals(uri) ){
return true ;
}
/** Token 验证 */
//在请求头中获取token,请求头的名字就是上面配置文件的header值
String token = request.getHeader(jwtConfigs.getHeader());
if(StringUtils.isEmpty(token)){
throw new SignatureException(jwtConfigs.getHeader()+ "不能为空");
}
Claims claims = null;
try{
claims = jwtConfigs.getTokenClaim(token);
if(claims == null || jwtConfigs.isTokenExpired(claims.getExpiration())){
throw new SignatureException(jwtConfigs.getHeader() + "失效,请重新登录。");
}
}catch (Exception e){
throw new SignatureException(jwtConfigs.getHeader() + "失效,请重新登录。");
}
/** 设置 identityId 用户身份ID */
request.setAttribute("identityId", claims.getSubject());
return true;
}
}
写好拦截器后,需要把拦截器注册到spring中
package com.emmmya.harin.config;
import com.emmmya.harin.common.utils.ShiroUtils;
import com.emmmya.harin.interceptor.CheckTokenInterceptor;
import com.emmmya.harin.interceptor.JwtTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
public class JwtWebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
//将jwt拦截器注册到WebMvc里。
//拦截所有的请求,所有请求都需要
registry.addInterceptor(jwtTokenInterceptor).addPathPatterns("/**");
}
好了,写个接口来测试下。
@Slf4j
@RestController
@Api("使用shiro+jwt来控制登录")
public class TokenController {
//注入生成token的工具类
@Autowired
JwtConfig jwtConfig;
/**
* 用户登录接口
* @return
*/
@RequestMapping (value = "/user/login")
public Result<Object> login(@ReuqestBody User user){
String token = JwtConfig.createToken(username);
return ResultUtils.success(token);
@RequestMapping(value ="testToken")
public Result<Object> testToken(){
return ResultUtils.success("你有token呀?NB喔。");
}
}
使用postman测试:
不携带Token请求时候。
这里就会抛出异常,至于异常处理,看你想怎么写了,写个异常页面单独处理也是可以的。
携带Token请求
在请求头上带上token后再请求。就发现放回的就是接口写好的字符串了。
PS:这个返回接口是单独写好的一个类。你们返回的结果可不一定跟我一样。但是只要看到接口返回的接口就好了。
集成shiro(配合jwt一起使用)
未完,待续.....