diff --git a/SpringBootShiroJWT/SpringBootShiroJWT.iml b/SpringBootShiroJWT/SpringBootShiroJWT.iml
new file mode 100644
index 0000000..e753cc9
--- /dev/null
+++ b/SpringBootShiroJWT/SpringBootShiroJWT.iml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SpringBootShiroJWT/pom.xml b/SpringBootShiroJWT/pom.xml
new file mode 100644
index 0000000..8c12464
--- /dev/null
+++ b/SpringBootShiroJWT/pom.xml
@@ -0,0 +1,82 @@
+
+
+
+ SpringBoot2
+ zz
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ SpringBootShiroJWT
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 1.4.0
+ 3.2.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+ org.apache.shiro
+ shiro-spring-boot-web-starter
+ ${shiro.spring.version}
+
+
+ com.auth0
+ java-jwt
+ ${jwt.auth0.version}
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.5
+
+
+ org.apache.commons
+ commons-lang3
+ 3.7
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ ${java.version}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/ShiroWebApplication.java b/SpringBootShiroJWT/src/main/java/com/github/demo/ShiroWebApplication.java
new file mode 100644
index 0000000..9ee3eea
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/ShiroWebApplication.java
@@ -0,0 +1,12 @@
+package com.github.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ShiroWebApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ShiroWebApplication.class, args);
+ }
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/DbShiroRealm.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/DbShiroRealm.java
new file mode 100644
index 0000000..8120685
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/DbShiroRealm.java
@@ -0,0 +1,67 @@
+package com.github.demo.configuration;
+
+import java.util.List;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.ByteSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.demo.dto.UserDto;
+import com.github.demo.service.UserService;
+
+public class DbShiroRealm extends AuthorizingRealm {
+ private final Logger log = LoggerFactory.getLogger(DbShiroRealm.class);
+
+ private static final String encryptSalt = "F12839WhsnnEV$#23b";
+ private UserService userService;
+
+ public DbShiroRealm(UserService userService) {
+ this.userService = userService;
+ this.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME));
+ }
+
+ @Override
+ public boolean supports(AuthenticationToken token) {
+ return token instanceof UsernamePasswordToken;
+ }
+ // 登录验证
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+ UsernamePasswordToken userpasswordToken = (UsernamePasswordToken)token;
+ String username = userpasswordToken.getUsername();
+ UserDto user = userService.getUserInfo(username);
+ if(user == null)
+ throw new AuthenticationException("用户名或者密码错误");
+
+ return new SimpleAuthenticationInfo(user, user.getEncryptPwd(), ByteSource.Util.bytes(encryptSalt), "dbRealm");
+ }
+
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+ UserDto user = (UserDto) principals.getPrimaryPrincipal();
+ List roles = user.getRoles();
+ if(roles == null) {
+ roles = userService.getUserRoles(user.getUserId());
+ user.setRoles(roles);
+ }
+ if (roles != null)
+ simpleAuthorizationInfo.addRoles(roles);
+
+ return simpleAuthorizationInfo;
+ }
+
+
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTCredentialsMatcher.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTCredentialsMatcher.java
new file mode 100644
index 0000000..ea517c9
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTCredentialsMatcher.java
@@ -0,0 +1,42 @@
+package com.github.demo.configuration;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.github.demo.dto.UserDto;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.credential.CredentialsMatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+
+public class JWTCredentialsMatcher implements CredentialsMatcher {
+
+ private final Logger log = LoggerFactory.getLogger(JWTCredentialsMatcher.class);
+
+ @Override
+ public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
+ String token = (String) authenticationToken.getCredentials();
+ Object stored = authenticationInfo.getCredentials();
+ String salt = stored.toString();
+
+ UserDto user = (UserDto)authenticationInfo.getPrincipals().getPrimaryPrincipal();
+ try {
+ Algorithm algorithm = Algorithm.HMAC256(salt);
+ JWTVerifier verifier = JWT.require(algorithm)
+ .withClaim("username", user.getUsername())
+ .build();
+ verifier.verify(token);
+ return true;
+ } catch (UnsupportedEncodingException | JWTVerificationException e) {
+ log.error("Token Error:{}", e.getMessage());
+ }
+
+ return false;
+ }
+
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTShiroRealm.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTShiroRealm.java
new file mode 100644
index 0000000..3c7efba
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTShiroRealm.java
@@ -0,0 +1,57 @@
+package com.github.demo.configuration;
+
+import org.apache.shiro.authc.*;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.demo.dto.UserDto;
+import com.github.demo.service.UserService;
+
+
+/**
+ * 自定义身份认证
+ * 基于HMAC( 散列消息认证码)的控制域
+ */
+
+public class JWTShiroRealm extends AuthorizingRealm {
+ private final Logger log = LoggerFactory.getLogger(JWTShiroRealm.class);
+
+ protected UserService userService;
+
+ public JWTShiroRealm(UserService userService){
+ this.userService = userService;
+ this.setCredentialsMatcher(new JWTCredentialsMatcher());
+ }
+
+ @Override
+ public boolean supports(AuthenticationToken token) {
+ return token instanceof JWTToken;
+ }
+
+ /**
+ * 认证信息.(身份验证) : Authentication 是用来验证用户身份
+ * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
+ JWTToken jwtToken = (JWTToken) authcToken;
+ String token = jwtToken.getToken();
+
+ UserDto user = userService.getJwtTokenInfo(JwtUtils.getUsername(token));
+ if(user == null)
+ throw new AuthenticationException("token过期,请重新登录");
+
+ SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getSalt(), "jwtRealm");
+
+ return authenticationInfo;
+ }
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ return new SimpleAuthorizationInfo();
+ }
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTToken.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTToken.java
new file mode 100644
index 0000000..875b980
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JWTToken.java
@@ -0,0 +1,42 @@
+package com.github.demo.configuration;
+
+import org.apache.shiro.authc.HostAuthenticationToken;
+
+public class JWTToken implements HostAuthenticationToken {
+ private static final long serialVersionUID = 9217639903967592166L;
+
+ private String token;
+ private String host;
+
+ public JWTToken(String token) {
+ this(token, null);
+ }
+
+ public JWTToken(String token, String host) {
+ this.token = token;
+ this.host = host;
+ }
+
+ public String getToken(){
+ return this.token;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return token;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return token;
+ }
+
+ @Override
+ public String toString(){
+ return token + ':' + host;
+ }
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JwtUtils.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JwtUtils.java
new file mode 100644
index 0000000..00fd3a2
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/JwtUtils.java
@@ -0,0 +1,82 @@
+package com.github.demo.configuration;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import org.apache.shiro.crypto.SecureRandomNumberGenerator;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+
+public class JwtUtils {
+
+ /**
+ * 获得token中的信息无需secret解密也能获得
+ * @return token中包含的签发时间
+ */
+ public static Date getIssuedAt(String token) {
+ try {
+ DecodedJWT jwt = JWT.decode(token);
+ return jwt.getIssuedAt();
+ } catch (JWTDecodeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 获得token中的信息无需secret解密也能获得
+ * @return token中包含的用户名
+ */
+ public static String getUsername(String token) {
+ try {
+ DecodedJWT jwt = JWT.decode(token);
+ return jwt.getClaim("username").asString();
+ } catch (JWTDecodeException e) {
+ return null;
+ }
+ }
+
+ /**
+ * 生成签名,expireTime后过期
+ * @param username 用户名
+ * @param time 过期时间s
+ * @return 加密的token
+ */
+ public static String sign(String username, String salt, long time) {
+ try {
+ Date date = new Date(System.currentTimeMillis()+time*1000);
+ Algorithm algorithm = Algorithm.HMAC256(salt);
+ // 附带username信息
+ return JWT.create()
+ .withClaim("username", username)
+ .withExpiresAt(date)
+ .withIssuedAt(new Date())
+ .sign(algorithm);
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ /**
+ * token是否过期
+ * @return true:过期
+ */
+ public static boolean isTokenExpired(String token) {
+ Date now = Calendar.getInstance().getTime();
+ DecodedJWT jwt = JWT.decode(token);
+ return jwt.getExpiresAt().before(now);
+ }
+
+ /**
+ * 生成随机盐,长度32位
+ * @return
+ */
+ public static String generateSalt(){
+ SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
+ String hex = secureRandom.nextBytes(16).toHex();
+ return hex;
+ }
+
+}
diff --git a/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/ResponseHeaderAdvice.java b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/ResponseHeaderAdvice.java
new file mode 100644
index 0000000..989ece4
--- /dev/null
+++ b/SpringBootShiroJWT/src/main/java/com/github/demo/configuration/ResponseHeaderAdvice.java
@@ -0,0 +1,61 @@
+package com.github.demo.configuration;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.http.server.ServletServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@ControllerAdvice
+public class ResponseHeaderAdvice implements ResponseBodyAdvice