Skip to content

Commit

Permalink
Spring Boot Shiro整合JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyouzhuguli committed Jun 18, 2019
1 parent c456b2c commit 697951d
Show file tree
Hide file tree
Showing 23 changed files with 1,152 additions and 0 deletions.
57 changes: 57 additions & 0 deletions 62.Spring-Boot-Shiro-JWT/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-shiro-jwt-demo</name>
<description>springboot整合shiro,jwt简单样例</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.demo;

import com.example.demo.properties.SystemProperties;
import org.springframework.boot.Banner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

/**
*
* @author MrBird
*/
@SpringBootApplication
@EnableConfigurationProperties(SystemProperties.class)
public class DemoApplication {

public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.example.demo.authentication;

import com.example.demo.properties.SystemProperties;
import com.example.demo.utils.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @author MrBird
*/
public class JWTFilter extends BasicHttpAuthenticationFilter {

private Logger log = LoggerFactory.getLogger(this.getClass());

private static final String TOKEN = "Token";

private AntPathMatcher pathMatcher = new AntPathMatcher();

@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
SystemProperties properties = SpringContextUtil.getBean(SystemProperties.class);
String[] anonUrl = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getAnonUrl(), ",");

boolean match = false;
for (String u : anonUrl) {
if (pathMatcher.match(u, httpServletRequest.getRequestURI()))
match = true;
}
if (match) return true;
if (isLoginAttempt(request, response)) {
return executeLogin(request, response);
}
return false;
}

@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader(TOKEN);
return token != null;
}

@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(TOKEN);
JWTToken jwtToken = new JWTToken(token);
try {
getSubject(request, response).login(jwtToken);
return true;
} catch (Exception e) {
log.error(e.getMessage());
return false;
}
}

/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个 option请求,这里我们给 option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.example.demo.authentication;

import org.apache.shiro.authc.AuthenticationToken;

/**
* JSON Web Token
*
* @author MrBird
*/
public class JWTToken implements AuthenticationToken {

private static final long serialVersionUID = 1282057025599826155L;

private String token;

private String exipreAt;

public JWTToken(String token) {
this.token = token;
}

public JWTToken(String token, String exipreAt) {
this.token = token;
this.exipreAt = exipreAt;
}

@Override
public Object getPrincipal() {
return token;
}

@Override
public Object getCredentials() {
return token;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

public String getExipreAt() {
return exipreAt;
}

public void setExipreAt(String exipreAt) {
this.exipreAt = exipreAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.example.demo.authentication;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.demo.properties.SystemProperties;
import com.example.demo.utils.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
*
* @author MrBird
*/
public class JWTUtil {

private static Logger log = LoggerFactory.getLogger(JWTUtil.class);

private static final long EXPIRE_TIME = SpringContextUtil.getBean(SystemProperties.class).getJwtTimeOut() * 1000;

/**
* 校验 token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
verifier.verify(token);
log.info("token is valid");
return true;
} catch (Exception e) {
log.info("token is invalid{}", e.getMessage());
return false;
}
}

/**
* 从 token中获取用户名
*
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
log.error("error:{}", e.getMessage());
return null;
}
}

/**
* 生成 token
*
* @param username 用户名
* @param secret 用户的密码
* @return token
*/
public static String sign(String username, String secret) {
try {
username = StringUtils.lowerCase(username);
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
log.error("error:{}", e);
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.example.demo.authentication;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;

/**
* Shiro 配置类
*
* @author MrBird
*/
@Configuration
public class ShiroConfig {

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置 securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);

// 在 Shiro过滤器链上加入 JWTFilter
LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
filters.put("jwt", new JWTFilter());
shiroFilterFactoryBean.setFilters(filters);

LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 所有请求都要经过 jwt过滤器
filterChainDefinitionMap.put("/**", "jwt");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}

@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 配置 SecurityManager,并注入 shiroRealm
securityManager.setRealm(shiroRealm());
return securityManager;
}

@Bean
public ShiroRealm shiroRealm() {
// 配置 Realm
return new ShiroRealm();
}

@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
Loading

0 comments on commit 697951d

Please sign in to comment.