第一步:新建項目
java哪個版本應該都行。我用的是java8。
第二步:配置maven倉庫(預先安裝apache)
第三步:pom.xml添加相關依賴,添加打包路徑與相關依賴
<!--添加內容-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<!--項目配置文件所在目錄-->
<resource>
<!--Mapper.xml也要編譯到項目-->
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
第四步:創建配置文件。在resources下創建application.properties
server.port=9999
#項目根路徑
server.servlet.context-path=/
#設置日誌級別
logging.level.com.sxjwt=DEBUG
logging.level.root=INFO
第五步:創建測試類與啓動類
測試類
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/demo")
public String demo(){
return "demo";
}
}
啓動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.jwt")
public class Boostarter {
public static void main(String[] args){
SpringApplication.run(Boostarter.class);
}
}
啓動項目。。。這是成功的情況
第六步:引入redis依賴(假設已經安裝並啓動redis)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
在application.properties中添加:
#redis
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
#可設置密碼
#spring.redis.password=
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=50
spring.redis.jedis.pool.min-idle=0
spring.redis.timeout=10000
redis測試類:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/get")
public Object get(String key){
return stringRedisTemplate.opsForValue().get(key);
}
@GetMapping("/set")
public Object set(String key,String value){
stringRedisTemplate.opsForValue().set(key,value);
return "ok";
}
}
效果:
第七步:引入jwt有關依賴:
<!--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.8.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
創建統一返回結果類checkResult:
import io.jsonwebtoken.Claims;
public class CheckResult {
private boolean Success;
private Claims claims;
private Integer errCode;
public Integer getErrCode() {
return errCode;
}
public void setErrCode(Integer errCode) {
this.errCode = errCode;
}
public boolean isSuccess() {
return Success;
}
public void setSuccess(boolean success) {
Success = success;
}
public Claims getClaims() {
return claims;
}
public void setClaims(Claims claims) {
this.claims = claims;
}
@Override
public String toString() {
return "CheckResult [Success="+ Success + ", claims="+ claims +
", errCode="+ errCode + "]";
}
}
創建jwt工具類:
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
public class JwtUtils {
private static final String ENTERPRISE="7th";
private static final String JWT_SECERT = "sxjwt";
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
/**
* 生成token
*/
public static String createToken(String userInfo, Long ttlMillis) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
SecretKey secretKey = generalKey();
JwtBuilder builder = Jwts.builder().setSubject(userInfo) // 主題
.setIssuer(ENTERPRISE) // 簽發者
.setIssuedAt(now) // 簽發時間
.signWith(signatureAlgorithm, secretKey); // 簽名算法以及密匙
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
builder.setExpiration(expDate); // 過期時間
}
return builder.compact();
}
/**
* 校驗token
*/
public static CheckResult validateJWT(String jwtStr) {
CheckResult checkResult = new CheckResult();
Claims claims = null;
try {
claims = parseJWT(jwtStr);
checkResult.setSuccess(true);
checkResult.setClaims(claims);
} catch (ExpiredJwtException e) {
checkResult.setErrCode(410);
checkResult.setSuccess(false);
} catch (SignatureException e) {
checkResult.setErrCode(510);
checkResult.setSuccess(false);
} catch (Exception e) {
checkResult.setErrCode(510);
checkResult.setSuccess(false);
}
return checkResult;
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
}
public static SecretKey generalKey() {
byte[] encodedKey = Base64.decodeBase64(JWT_SECERT);
SecretKey key = new SecretKeySpec(encodedKey, 0,
encodedKey.length, "AES");
return key;
}
}
創建自定義註解:@auth
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Auth {
String[] Roles() default {"admin"};
}
創建user實體類:
import java.util.List;
public class User {
private Integer id;
private String name;
private String pwd;
private List<String>roles;
private String token;
private String refleshToken;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public List<String>getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRefleshToken() {
return refleshToken;
}
public void setRefleshToken(String refleshToken) {
this.refleshToken = refleshToken;
}
@Override
public String toString() {
return "User{"+
"id="+ id +
", name='"+ name + '\'' +
", pwd='"+ pwd + '\'' +
", roles="+ roles +
", token='"+ token + '\'' +
", refleshToken='"+ refleshToken + '\'' +
'}';
}
}
創建json工具類:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JsonUtils {
// 定義jackson對象
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 將對象轉換成json字符串。
* <p>Title: pojoToJson</p>
* <p>Description: </p>
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 將json結果集轉化為對象
*
* @param jsonData json數據
* @param clazz 對象中的object類型
* @return
*/
public static <T>T jsonToPojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 將json數據轉換成pojo對象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
* @param jsonData
* @param beanType
* @return
*/
public static <T>List<T>jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T>list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
創建登錄控制器LoginController
import com.alibaba.fastjson.JSONObject;
import com.sxjwt.pojo.User;
import com.sxjwt.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
public class LoginController {
@Autowired
StringRedisTemplate stringRedisTemplate;
@RequestMapping("/login")
public Object login(String name,String pwd) throws Exception {
if(StringUtils.isEmpty(name)||StringUtils.isEmpty(pwd)){
throw new Exception("用户名或密碼錯誤");
}
String md5 = getMD5(pwd);
User user = selectUser(name, md5);
if(user == null){
throw new Exception("用户名或密碼錯誤");
}
//設置Token
user.setPwd(null);
user.setToken(JwtUtils.createToken(JSONObject.toJSONString(user),1000*20L));
user.setRefleshToken(JwtUtils.createToken(JSONObject.toJSONString(user),1000*60*60*24*365L));
//存入Redis
stringRedisTemplate.opsForValue().set(user.getId()+"",user.getToken());
return user.toString();
}
//查詢用户
private User selectUser(String name, String pwd) {
for (User user : systemUsers) {
if (user.getName().equals(name) &&user.getPwd().equals(pwd)) {
User u =new User();
u.setId(user.getId());
u.setName(user.getName());
u.setRoles(user.getRoles());
return u;
}
}
return null;
}
//密碼加密
private static String getMD5(String value) {
String base = value + "/s/"+ "sds2d2ff";
return DigestUtils.md5DigestAsHex(base.getBytes());
}
//模擬數據庫查詢
//添加虛擬用户
public static List<User>systemUsers = new ArrayList();
static {
User user1 = new User();
user1.setId(1);
user1.setName("admin");
//123
user1.setPwd("51b274f7949d4ffc4c018283e1b679ea");
user1.setRoles(Arrays.asList(new String[]{"admin"}));
User user2 = new User();
user2.setId(2);
user2.setName("zhang");
//123
user2.setPwd("51b274f7949d4ffc4c018283e1b679ea");
user2.setRoles(Arrays.asList(new String[]{"admin", "user"}));
systemUsers.add(user1);
systemUsers.add(user2);
}
}
攔截器:
MyInterceptor 和 interceptorConfig
MyInterceptor:
import com.alibaba.fastjson.JSONObject;
import com.sxjwt.LoginController;
import com.sxjwt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String userId = request.getHeader("userId");
String token = request.getHeader("token");
String refleshToken = request.getHeader("refleshToken");
//校驗token是否存在
if (StringUtils.isEmpty(token) || StringUtils.isEmpty(userId)) {
returnErrorResponse(response, "無權限訪問1");
return false;
}
String userCurrentToken = stringRedisTemplate.opsForValue().get(userId);
if (userCurrentToken == null || !userCurrentToken.equals(token)) {
returnErrorResponse(response, "無權限訪問2");
return false;
}
//校驗token是否過期
CheckResult checkResult = JwtUtils.validateJWT(token);
if (checkResult.isSuccess()) {
//校驗權限
if (validateRights(checkResult, response, handler)) {
//放行 將token 返回
response.setHeader("token", token);
response.setHeader("refleshToken", refleshToken);
return true;
} else {
returnErrorResponse(response, "無權限訪問3");
return false;
}
} else {
//刷新token
CheckResult checkResult1 = JwtUtils.validateJWT(refleshToken);
User user = JSONObject.parseObject(checkResult1.getClaims().getSubject(), User.class);
if (!userId.equals(user.getId() + "")) {
returnErrorResponse(response, "無權限訪問4");
return false;
}
User u = getOneUser(user.getId());
if (u == null) {
returnErrorResponse(response, "無權限訪問5");
return false;
}
//設置Token
String newToken = JwtUtils.createToken(JSONObject.toJSONString(u), 1000 * 60 * 60 * 2L);
u.setToken(newToken);
response.setHeader("token", newToken);
response.setHeader("refleshToken", JwtUtils.createToken(JSONObject.toJSONString(u), -1L));
//存入Redis
stringRedisTemplate.opsForValue().set(u.getId() + "", newToken);
//校驗權限
List<String>roles = u.getRoles();
if (validateRights(roles, response, handler)) {
return true;
} else {
returnErrorResponse(response, "無權限訪問6");
return false;
}
}
}
public void returnErrorResponse(HttpServletResponse response, Object result) {
OutputStream out = null;
try {
response.setStatus(403);
response.setCharacterEncoding("utf-8");
response.setContentType("text/json");
out = response.getOutputStream();
out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private boolean validateRights(List<String> userRoles, HttpServletResponse response, Object handler) {
try {
Class<? extends Method>targetClass = ((HandlerMethod) handler).getMethod().getClass();
Auth auth = null;
auth = targetClass.getAnnotation(Auth.class);
if (auth == null) {
Method method = ((HandlerMethod) handler).getMethod();
auth = method.getAnnotation(Auth.class);
}
//不需要權限校驗
if (auth == null) {
return true;
}
//校驗用户是否有權限
String[] roles = auth.Roles();
for (String role : userRoles) {
if (Arrays.asList(roles).contains(role)) {
return true;
}
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private boolean validateRights(CheckResult checkResult, HttpServletResponse response, Object handler) {
try {
String stringUser = checkResult.getClaims().getSubject();// 獲取token信息
User user = JSONObject.parseObject(stringUser, User.class);
List<String>userRoles = user.getRoles();
Class<? extends Method>targetClass = ((HandlerMethod) handler).getMethod().getClass();
Auth auth = null;
auth = targetClass.getAnnotation(Auth.class);
if (auth == null) {
Method method = ((HandlerMethod) handler).getMethod();
auth = method.getAnnotation(Auth.class);
}
//不需要權限校驗
if (auth == null) {
return true;
}
//校驗用户是否有權限
String[] roles = auth.Roles();
for (String role : userRoles) {
if (Arrays.asList(roles).contains(role)) {
return true;
}
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private User getOneUser(int id) {
List<User>systemUsers = LoginController.systemUsers;
for (User user : systemUsers) {
if (id == user.getId()) {
User u = new User();
u.setId(user.getId());
u.setName(user.getName());
u.setRoles(user.getRoles());
return u;
}
}
return null;
}
}
InterceptorConfig:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//註解:決定是否啓動攔截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor MyInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加限流攔截器
registry.addInterceptor(MyInterceptor)
.addPathPatterns("/**")
//放行的路徑
.excludePathPatterns("/login");
}
}
未登錄和已登錄效果對比:
結束~~~
技術交流:753242533