├── security-jwt-demo ├── bin │ ├── main │ │ ├── application.properties │ │ └── com │ │ │ └── github │ │ │ └── demo │ │ │ ├── service │ │ │ ├── JwtUserService.class │ │ │ └── JwtAuthenticationProvider.class │ │ │ ├── SecurityJwtDemoApplication.class │ │ │ ├── filter │ │ │ ├── OptionsRequestFilter.class │ │ │ ├── JwtAuthenticationFilter.class │ │ │ └── MyUsernamePasswordAuthenticationFilter.class │ │ │ ├── controller │ │ │ └── ArticleController.class │ │ │ └── configuration │ │ │ ├── JwtLoginConfigurer.class │ │ │ ├── WebSecurityConfig.class │ │ │ ├── JsonLoginConfigurer.class │ │ │ ├── JsonLoginSuccessHandler.class │ │ │ ├── JwtAuthenticationToken.class │ │ │ ├── TokenClearLogoutHandler.class │ │ │ ├── JwtRefreshSuccessHandler.class │ │ │ └── HttpStatusLoginFailureHandler.class │ └── test │ │ └── com │ │ └── github │ │ └── springboot │ │ └── securityjwtdemo │ │ └── SecurityJwtDemoApplicationTests.class ├── settings.gradle ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── demo │ │ │ ├── SecurityJwtDemoApplication.java │ │ │ ├── configuration │ │ │ ├── HttpStatusLoginFailureHandler.java │ │ │ ├── JsonLoginSuccessHandler.java │ │ │ ├── TokenClearLogoutHandler.java │ │ │ ├── JwtAuthenticationToken.java │ │ │ ├── JwtLoginConfigurer.java │ │ │ ├── JsonLoginConfigurer.java │ │ │ ├── JwtRefreshSuccessHandler.java │ │ │ └── WebSecurityConfig.java │ │ │ ├── controller │ │ │ └── ArticleController.java │ │ │ ├── filter │ │ │ ├── OptionsRequestFilter.java │ │ │ ├── MyUsernamePasswordAuthenticationFilter.java │ │ │ └── JwtAuthenticationFilter.java │ │ │ └── service │ │ │ ├── JwtAuthenticationProvider.java │ │ │ └── JwtUserService.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── springboot │ │ └── securityjwtdemo │ │ └── SecurityJwtDemoApplicationTests.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle ├── gradlew.bat └── gradlew ├── README.md ├── shiro-jwt-demo ├── README.md ├── src │ └── main │ │ ├── resources │ │ ├── application.properties │ │ └── logback-spring.xml │ │ └── java │ │ └── com │ │ └── github │ │ └── demo │ │ ├── ShiroWebApplication.java │ │ ├── controller │ │ ├── AsyncRequestController.java │ │ ├── ArticleController.java │ │ └── LoginController.java │ │ ├── configuration │ │ ├── WebConfiguration.java │ │ ├── JWTToken.java │ │ ├── JWTCredentialsMatcher.java │ │ ├── JWTShiroRealm.java │ │ ├── JwtUtils.java │ │ ├── DbShiroRealm.java │ │ ├── ResponseHeaderAdvice.java │ │ └── ShiroConfig.java │ │ ├── dto │ │ ├── UserDto.java │ │ └── ArticleDto.java │ │ ├── filter │ │ ├── AnyRolesAuthorizationFilter.java │ │ └── JwtAuthFilter.java │ │ └── service │ │ └── UserService.java ├── .gitignore └── pom.xml ├── redis-tx-demo ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── springboot │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ ├── RedisService.java │ │ │ ├── RedisRunner.java │ │ │ ├── DemoController.java │ │ │ └── RedisConfiguration.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.java ├── .gitignore └── pom.xml └── .gitignore /security-jwt-demo/bin/main/application.properties: -------------------------------------------------------------------------------- 1 | #logging.level.root=DEBUG -------------------------------------------------------------------------------- /security-jwt-demo/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'security-jwt-demo' 2 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #logging.level.root=DEBUG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Demo 2 | 3 | ####redis-tx-demo 4 | Spring Boot中使用Redis事务的demo 5 | 6 | ####shiro-jwt-demo 7 | Spring Boot中使用shiro和JWT做Rest服务访问认证的demo 8 | -------------------------------------------------------------------------------- /security-jwt-demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /shiro-jwt-demo/README.md: -------------------------------------------------------------------------------- 1 | # Shiro and JWT 2 | 结合SpringBoot、Shiro和JWT实现的无状态用户访问控制 3 | - 使用shiro做登录和权限验证 4 | - 使用jwt做无状态应用的用户校验 5 | - 禁用session 6 | - 支持跨域访问 7 | 8 | -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/service/JwtUserService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/service/JwtUserService.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/SecurityJwtDemoApplication.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/SecurityJwtDemoApplication.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/filter/OptionsRequestFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/filter/OptionsRequestFilter.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/controller/ArticleController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/controller/ArticleController.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/filter/JwtAuthenticationFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/filter/JwtAuthenticationFilter.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/JwtLoginConfigurer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/JwtLoginConfigurer.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/WebSecurityConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/WebSecurityConfig.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/JsonLoginConfigurer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/JsonLoginConfigurer.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/service/JwtAuthenticationProvider.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/service/JwtAuthenticationProvider.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/JsonLoginSuccessHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/JsonLoginSuccessHandler.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/JwtAuthenticationToken.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/JwtAuthenticationToken.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/TokenClearLogoutHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/TokenClearLogoutHandler.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/JwtRefreshSuccessHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/JwtRefreshSuccessHandler.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/configuration/HttpStatusLoginFailureHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/configuration/HttpStatusLoginFailureHandler.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/main/com/github/demo/filter/MyUsernamePasswordAuthenticationFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/main/com/github/demo/filter/MyUsernamePasswordAuthenticationFilter.class -------------------------------------------------------------------------------- /security-jwt-demo/bin/test/com/github/springboot/securityjwtdemo/SecurityJwtDemoApplicationTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chilexun/springboot-demo/HEAD/security-jwt-demo/bin/test/com/github/springboot/securityjwtdemo/SecurityJwtDemoApplicationTests.class -------------------------------------------------------------------------------- /redis-tx-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #redis 2 | spring.redis.host=localhost 3 | spring.redis.port=6379 4 | spring.redis.pool.max-active=5 5 | spring.redis.pool.max-idle=1 6 | spring.redis.pool.min-idle=1 7 | spring.redis.pool.max-wait=5000 8 | spring.redis.timeout=30000 9 | 10 | -------------------------------------------------------------------------------- /security-jwt-demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 06 12:27:20 CET 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip 7 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #redis 2 | spring.redis.host=localhost 3 | spring.redis.port=6379 4 | spring.redis.lettuce.pool.max-active=5 5 | spring.redis.lettuce.pool.max-idle=1 6 | spring.redis.lettuce.pool.min-idle=1 7 | spring.redis.lettuce.pool.max-wait=5000 8 | spring.redis.timeout=30000 9 | -------------------------------------------------------------------------------- /shiro-jwt-demo/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /redis-tx-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /security-jwt-demo/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | /out/ 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/ShiroWebApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ShiroWebApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ShiroWebApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | 27 | .mvn/ 28 | mvnw 29 | mvnw.cmd 30 | 31 | comm/ -------------------------------------------------------------------------------- /redis-tx-demo/src/main/java/com/github/springboot/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/SecurityJwtDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SecurityJwtDemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SecurityJwtDemoApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /redis-tx-demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /security-jwt-demo/src/test/java/com/github/springboot/securityjwtdemo/SecurityJwtDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.securityjwtdemo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SecurityJwtDemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/controller/AsyncRequestController.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.controller; 2 | 3 | import java.util.concurrent.Callable; 4 | 5 | import org.apache.shiro.SecurityUtils; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import com.github.demo.dto.UserDto; 10 | 11 | @RestController 12 | public class AsyncRequestController { 13 | 14 | @GetMapping("/async") 15 | public Callable doAsync(){ 16 | return ()->{ 17 | Thread.sleep(5000); 18 | return (UserDto)SecurityUtils.getSubject().getPrincipal(); 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/controller/ArticleController.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import com.github.demo.dto.ArticleDto; 9 | 10 | @RestController 11 | @RequestMapping("/article") 12 | public class ArticleController { 13 | 14 | @GetMapping("/list") 15 | public ResponseEntity> list(){ 16 | return null; 17 | } 18 | 19 | @GetMapping("/{id}") 20 | public ResponseEntity read(@PathVariable Long id){ 21 | return null; 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /redis-tx-demo/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | redis-demo 5 | 6 | 7 | 8 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%level][%thread]:%logger{50} [%method:%line] %msg%n 9 | utf-8 10 | 11 | 12 | debug 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | shiro-jwt-demo 5 | 6 | 7 | 8 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%level][%thread]:%logger{50} [%method:%line] %msg%n 9 | utf-8 10 | 11 | 12 | debug 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /redis-tx-demo/src/main/java/com/github/springboot/demo/RedisService.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.demo; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | @Service 10 | public class RedisService { 11 | 12 | private StringRedisTemplate template; 13 | 14 | public RedisService(StringRedisTemplate template) { 15 | this.template = template; 16 | } 17 | 18 | @Transactional 19 | public String put() { 20 | int i = (int)(Math.random() * 100); 21 | template.opsForValue().set("key"+i, "value"+i, 300, TimeUnit.SECONDS); 22 | return "success "+"key"+i; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /redis-tx-demo/src/main/java/com/github/springboot/demo/RedisRunner.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.demo; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.data.redis.core.StringRedisTemplate; 5 | import org.springframework.stereotype.Service; 6 | 7 | //@Service 8 | public class RedisRunner implements CommandLineRunner{ 9 | private final StringRedisTemplate template; 10 | 11 | public RedisRunner(StringRedisTemplate template) { 12 | this.template = template; 13 | } 14 | 15 | 16 | @Override 17 | public void run(String... args) throws Exception { 18 | for(int i=0; i<10; i++) { 19 | template.opsForValue().set("key"+i, "value"+i, 300); 20 | System.out.println("==========Set value"+i+" to redis=========="); 21 | } 22 | System.out.println("finish"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/HttpStatusLoginFailureHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.security.core.AuthenticationException; 11 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 12 | 13 | public class HttpStatusLoginFailureHandler implements AuthenticationFailureHandler{ 14 | 15 | @Override 16 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 17 | AuthenticationException exception) throws IOException, ServletException { 18 | response.setStatus(HttpStatus.UNAUTHORIZED.value()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.util.concurrent.Executors; 4 | 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; 7 | import org.springframework.web.servlet.config.annotation.*; 8 | 9 | @Configuration 10 | public class WebConfiguration extends WebMvcConfigurationSupport{ 11 | 12 | @Override 13 | public void addCorsMappings(CorsRegistry registry) { 14 | registry.addMapping("/**") 15 | .allowedHeaders("*") 16 | .allowedMethods("*") 17 | .allowedOrigins("*"); 18 | } 19 | 20 | @Override 21 | protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { 22 | configurer.setTaskExecutor(new ConcurrentTaskExecutor(Executors.newFixedThreadPool(3))); 23 | configurer.setDefaultTimeout(30000); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/controller/ArticleController.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.controller; 2 | 3 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping("article") 13 | public class ArticleController { 14 | 15 | @GetMapping("{id}") 16 | public String load(@PathVariable Long id) { 17 | return "This is my first blog"; 18 | } 19 | 20 | @PostMapping("add") 21 | public void create(@AuthenticationPrincipal UserDetails user) { 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/filter/OptionsRequestFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.filter; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.springframework.web.filter.OncePerRequestFilter; 11 | 12 | public class OptionsRequestFilter extends OncePerRequestFilter{ 13 | 14 | @Override 15 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 16 | throws ServletException, IOException { 17 | if(request.getMethod().equals("OPTIONS")) { 18 | response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD"); 19 | response.setHeader("Access-Control-Allow-Headers", response.getHeader("Access-Control-Request-Headers")); 20 | return; 21 | } 22 | filterChain.doFilter(request, response); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /redis-tx-demo/src/main/java/com/github/springboot/demo/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.demo; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | public class DemoController { 11 | 12 | private StringRedisTemplate template; 13 | private RedisService redisService; 14 | 15 | public DemoController(StringRedisTemplate template,RedisService redisService) { 16 | this.template = template; 17 | this.redisService = redisService; 18 | } 19 | 20 | @GetMapping("/put") 21 | public String redisSet() { 22 | int i = (int)(Math.random() * 100); 23 | template.opsForValue().set("key"+i, "value"+i, 300, TimeUnit.SECONDS); 24 | return "success "+"key"+i; 25 | } 26 | 27 | @GetMapping("/puttx") 28 | public String redisTxSet() { 29 | return redisService.put(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /security-jwt-demo/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '2.0.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | apply plugin: 'io.spring.dependency-management' 17 | 18 | group = 'com.github.springboot' 19 | version = '0.0.1-SNAPSHOT' 20 | sourceCompatibility = 1.8 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | 27 | dependencies { 28 | compile('org.springframework.boot:spring-boot-starter-security') 29 | compile('org.springframework.boot:spring-boot-starter-web') 30 | compile('org.apache.commons:commons-lang3:3.8') 31 | compile('com.auth0:java-jwt:3.4.0') 32 | compile('com.alibaba:fastjson:1.2.47') 33 | 34 | testCompile('org.springframework.boot:spring-boot-starter-test') 35 | testCompile('org.springframework.security:spring-security-test') 36 | } 37 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/JWTToken.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.apache.shiro.authc.HostAuthenticationToken; 4 | 5 | public class JWTToken implements HostAuthenticationToken { 6 | private static final long serialVersionUID = 9217639903967592166L; 7 | 8 | private String token; 9 | private String host; 10 | 11 | public JWTToken(String token) { 12 | this(token, null); 13 | } 14 | 15 | public JWTToken(String token, String host) { 16 | this.token = token; 17 | this.host = host; 18 | } 19 | 20 | public String getToken(){ 21 | return this.token; 22 | } 23 | 24 | public String getHost() { 25 | return host; 26 | } 27 | 28 | @Override 29 | public Object getPrincipal() { 30 | return token; 31 | } 32 | 33 | @Override 34 | public Object getCredentials() { 35 | return token; 36 | } 37 | 38 | @Override 39 | public String toString(){ 40 | return token + ':' + host; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /redis-tx-demo/src/main/java/com/github/springboot/demo/RedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.springboot.demo; 2 | 3 | import java.sql.SQLException; 4 | 5 | import javax.sql.DataSource; 6 | 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; 10 | import org.springframework.data.redis.core.StringRedisTemplate; 11 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 12 | import org.springframework.transaction.PlatformTransactionManager; 13 | 14 | @Configuration 15 | public class RedisConfiguration { 16 | 17 | @Bean 18 | public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 19 | StringRedisTemplate template = new StringRedisTemplate(); 20 | template.setConnectionFactory(redisConnectionFactory); 21 | template.setEnableTransactionSupport(true); 22 | return template; 23 | } 24 | 25 | @Bean 26 | public PlatformTransactionManager transactionManager(DataSource dataSource) throws SQLException { 27 | return new DataSourceTransactionManager(dataSource); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/JsonLoginSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 12 | 13 | import com.github.demo.service.JwtUserService; 14 | 15 | public class JsonLoginSuccessHandler implements AuthenticationSuccessHandler{ 16 | 17 | private JwtUserService jwtUserService; 18 | 19 | public JsonLoginSuccessHandler(JwtUserService jwtUserService) { 20 | this.jwtUserService = jwtUserService; 21 | } 22 | 23 | @Override 24 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 25 | Authentication authentication) throws IOException, ServletException { 26 | String token = jwtUserService.saveUserLoginInfo((UserDetails)authentication.getPrincipal()); 27 | response.setHeader("Authorization", token); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/TokenClearLogoutHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.web.authentication.logout.LogoutHandler; 9 | 10 | import com.github.demo.service.JwtUserService; 11 | 12 | public class TokenClearLogoutHandler implements LogoutHandler { 13 | 14 | private JwtUserService jwtUserService; 15 | 16 | public TokenClearLogoutHandler(JwtUserService jwtUserService) { 17 | this.jwtUserService = jwtUserService; 18 | } 19 | 20 | @Override 21 | public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { 22 | clearToken(authentication); 23 | } 24 | 25 | protected void clearToken(Authentication authentication) { 26 | if(authentication == null) 27 | return; 28 | UserDetails user = (UserDetails)authentication.getPrincipal(); 29 | if(user!=null && user.getUsername()!=null) 30 | jwtUserService.deleteUserLoginInfo(user.getUsername()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * 用户对象 8 | */ 9 | public class UserDto implements Serializable { 10 | private static final long serialVersionUID = -9077975168976887742L; 11 | 12 | private String username; 13 | private char[] password; 14 | private String encryptPwd; 15 | private Long userId; 16 | private String salt; 17 | private List roles; 18 | 19 | public String getUsername() { 20 | return username; 21 | } 22 | 23 | public void setUsername(String username) { 24 | this.username = username; 25 | } 26 | 27 | public Long getUserId() { 28 | return userId; 29 | } 30 | 31 | public void setUserId(Long userId) { 32 | this.userId = userId; 33 | } 34 | 35 | public char[] getPassword() { 36 | return password; 37 | } 38 | 39 | public void setPassword(char[] password) { 40 | this.password = password; 41 | } 42 | 43 | public String getSalt() { 44 | return salt; 45 | } 46 | 47 | public void setSalt(String salt) { 48 | this.salt = salt; 49 | } 50 | 51 | public List getRoles() { 52 | return roles; 53 | } 54 | 55 | public void setRoles(List roles) { 56 | this.roles = roles; 57 | } 58 | 59 | public String getEncryptPwd() { 60 | return encryptPwd; 61 | } 62 | 63 | public void setEncryptPwd(String encryptPwd) { 64 | this.encryptPwd = encryptPwd; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/JwtAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | 6 | import org.springframework.security.authentication.AbstractAuthenticationToken; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | 10 | import com.auth0.jwt.interfaces.DecodedJWT; 11 | 12 | public class JwtAuthenticationToken extends AbstractAuthenticationToken { 13 | private static final long serialVersionUID = 3981518947978158945L; 14 | 15 | private UserDetails principal; 16 | private String credentials; 17 | private DecodedJWT token; 18 | 19 | public JwtAuthenticationToken(DecodedJWT token) { 20 | super(Collections.emptyList()); 21 | this.token = token; 22 | } 23 | 24 | public JwtAuthenticationToken(UserDetails principal, DecodedJWT token, Collection authorities) { 25 | super(authorities); 26 | this.principal = principal; 27 | this.token = token; 28 | } 29 | 30 | @Override 31 | public void setDetails(Object details) { 32 | super.setDetails(details); 33 | this.setAuthenticated(true); 34 | } 35 | 36 | @Override 37 | public Object getCredentials() { 38 | return credentials; 39 | } 40 | 41 | @Override 42 | public Object getPrincipal() { 43 | return principal; 44 | } 45 | 46 | public DecodedJWT getToken() { 47 | return token; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/JwtLoginConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.springframework.security.authentication.AuthenticationManager; 4 | import org.springframework.security.config.annotation.web.HttpSecurityBuilder; 5 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 6 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 7 | import org.springframework.security.web.authentication.logout.LogoutFilter; 8 | import com.github.demo.filter.JwtAuthenticationFilter; 9 | 10 | public class JwtLoginConfigurer, B extends HttpSecurityBuilder> extends AbstractHttpConfigurer { 11 | 12 | private JwtAuthenticationFilter authFilter; 13 | 14 | public JwtLoginConfigurer() { 15 | this.authFilter = new JwtAuthenticationFilter(); 16 | } 17 | 18 | @Override 19 | public void configure(B http) throws Exception { 20 | authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); 21 | authFilter.setAuthenticationFailureHandler(new HttpStatusLoginFailureHandler()); 22 | 23 | JwtAuthenticationFilter filter = postProcess(authFilter); 24 | http.addFilterBefore(filter, LogoutFilter.class); 25 | } 26 | 27 | public JwtLoginConfigurer permissiveRequestUrls(String ... urls){ 28 | authFilter.setPermissiveUrl(urls); 29 | return this; 30 | } 31 | 32 | public JwtLoginConfigurer tokenValidSuccessHandler(AuthenticationSuccessHandler successHandler){ 33 | authFilter.setAuthenticationSuccessHandler(successHandler); 34 | return this; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/JWTCredentialsMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.JWTVerifier; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.exceptions.JWTVerificationException; 7 | import com.github.demo.dto.UserDto; 8 | 9 | import org.apache.shiro.authc.AuthenticationInfo; 10 | import org.apache.shiro.authc.AuthenticationToken; 11 | import org.apache.shiro.authc.credential.CredentialsMatcher; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.io.UnsupportedEncodingException; 16 | 17 | public class JWTCredentialsMatcher implements CredentialsMatcher { 18 | 19 | private final Logger log = LoggerFactory.getLogger(JWTCredentialsMatcher.class); 20 | 21 | @Override 22 | public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) { 23 | String token = (String) authenticationToken.getCredentials(); 24 | Object stored = authenticationInfo.getCredentials(); 25 | String salt = stored.toString(); 26 | 27 | UserDto user = (UserDto)authenticationInfo.getPrincipals().getPrimaryPrincipal(); 28 | try { 29 | Algorithm algorithm = Algorithm.HMAC256(salt); 30 | JWTVerifier verifier = JWT.require(algorithm) 31 | .withClaim("username", user.getUsername()) 32 | .build(); 33 | verifier.verify(token); 34 | return true; 35 | } catch (UnsupportedEncodingException | JWTVerificationException e) { 36 | log.error("Token Error:{}", e.getMessage()); 37 | } 38 | 39 | return false; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/JsonLoginConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.springframework.security.authentication.AuthenticationManager; 4 | import org.springframework.security.config.annotation.web.HttpSecurityBuilder; 5 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 6 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 7 | import org.springframework.security.web.authentication.logout.LogoutFilter; 8 | import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; 9 | import com.github.demo.filter.MyUsernamePasswordAuthenticationFilter; 10 | 11 | public class JsonLoginConfigurer, B extends HttpSecurityBuilder> extends AbstractHttpConfigurer { 12 | 13 | private MyUsernamePasswordAuthenticationFilter authFilter; 14 | 15 | public JsonLoginConfigurer() { 16 | this.authFilter = new MyUsernamePasswordAuthenticationFilter(); 17 | } 18 | 19 | @Override 20 | public void configure(B http) throws Exception { 21 | authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); 22 | authFilter.setAuthenticationFailureHandler(new HttpStatusLoginFailureHandler()); 23 | authFilter.setSessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()); 24 | 25 | MyUsernamePasswordAuthenticationFilter filter = postProcess(authFilter); 26 | http.addFilterAfter(filter, LogoutFilter.class); 27 | } 28 | 29 | public JsonLoginConfigurer loginSuccessHandler(AuthenticationSuccessHandler authSuccessHandler){ 30 | authFilter.setAuthenticationSuccessHandler(authSuccessHandler); 31 | return this; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/JwtRefreshSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.io.IOException; 4 | import java.time.LocalDateTime; 5 | import java.time.ZoneId; 6 | import java.util.Date; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.userdetails.UserDetails; 14 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 15 | 16 | import com.auth0.jwt.interfaces.DecodedJWT; 17 | import com.github.demo.service.JwtUserService; 18 | 19 | public class JwtRefreshSuccessHandler implements AuthenticationSuccessHandler{ 20 | 21 | private static final int tokenRefreshInterval = 300; //刷新间隔5分钟 22 | 23 | private JwtUserService jwtUserService; 24 | 25 | public JwtRefreshSuccessHandler(JwtUserService jwtUserService) { 26 | this.jwtUserService = jwtUserService; 27 | } 28 | 29 | @Override 30 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 31 | Authentication authentication) throws IOException, ServletException { 32 | DecodedJWT jwt = ((JwtAuthenticationToken)authentication).getToken(); 33 | boolean shouldRefresh = shouldTokenRefresh(jwt.getIssuedAt()); 34 | if(shouldRefresh) { 35 | String newToken = jwtUserService.saveUserLoginInfo((UserDetails)authentication.getPrincipal()); 36 | response.setHeader("Authorization", newToken); 37 | } 38 | } 39 | 40 | protected boolean shouldTokenRefresh(Date issueAt){ 41 | LocalDateTime issueTime = LocalDateTime.ofInstant(issueAt.toInstant(), ZoneId.systemDefault()); 42 | return LocalDateTime.now().minusSeconds(tokenRefreshInterval).isAfter(issueTime); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/JWTShiroRealm.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.apache.shiro.authc.*; 4 | import org.apache.shiro.authz.AuthorizationInfo; 5 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 6 | import org.apache.shiro.realm.AuthorizingRealm; 7 | import org.apache.shiro.subject.PrincipalCollection; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.github.demo.dto.UserDto; 12 | import com.github.demo.service.UserService; 13 | 14 | 15 | /** 16 | * 自定义身份认证 17 | * 基于HMAC( 散列消息认证码)的控制域 18 | */ 19 | 20 | public class JWTShiroRealm extends AuthorizingRealm { 21 | private final Logger log = LoggerFactory.getLogger(JWTShiroRealm.class); 22 | 23 | protected UserService userService; 24 | 25 | public JWTShiroRealm(UserService userService){ 26 | this.userService = userService; 27 | this.setCredentialsMatcher(new JWTCredentialsMatcher()); 28 | } 29 | 30 | @Override 31 | public boolean supports(AuthenticationToken token) { 32 | return token instanceof JWTToken; 33 | } 34 | 35 | /** 36 | * 认证信息.(身份验证) : Authentication 是用来验证用户身份 37 | * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。 38 | */ 39 | @Override 40 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { 41 | JWTToken jwtToken = (JWTToken) authcToken; 42 | String token = jwtToken.getToken(); 43 | 44 | UserDto user = userService.getJwtTokenInfo(JwtUtils.getUsername(token)); 45 | if(user == null) 46 | throw new AuthenticationException("token过期,请重新登录"); 47 | 48 | SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getSalt(), "jwtRealm"); 49 | 50 | return authenticationInfo; 51 | } 52 | 53 | @Override 54 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 55 | return new SimpleAuthorizationInfo(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/filter/AnyRolesAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.filter; 2 | 3 | import org.apache.commons.lang3.BooleanUtils; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.shiro.subject.Subject; 6 | import org.apache.shiro.web.filter.authz.AuthorizationFilter; 7 | import org.apache.shiro.web.util.WebUtils; 8 | 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | public class AnyRolesAuthorizationFilter extends AuthorizationFilter { 15 | 16 | @Override 17 | protected void postHandle(ServletRequest request, ServletResponse response){ 18 | request.setAttribute("anyRolesAuthFilter.FILTERED", true); 19 | } 20 | 21 | @Override 22 | protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception { 23 | Boolean afterFiltered = (Boolean)(servletRequest.getAttribute("anyRolesAuthFilter.FILTERED")); 24 | if( BooleanUtils.isTrue(afterFiltered)) 25 | return true; 26 | 27 | Subject subject = getSubject(servletRequest, servletResponse); 28 | String[] rolesArray = (String[]) mappedValue; 29 | if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问 30 | return true; 31 | } 32 | for (String role : rolesArray) { 33 | if (subject.hasRole(role)) //若当前用户是rolesArray中的任何一个,则有权限访问 34 | return true; 35 | } 36 | return false; 37 | } 38 | 39 | @Override 40 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { 41 | HttpServletResponse httpResponse = WebUtils.toHttp(response); 42 | httpResponse.setCharacterEncoding("UTF-8"); 43 | httpResponse.setContentType("application/json;charset=utf-8"); 44 | httpResponse.setStatus(HttpStatus.SC_UNAUTHORIZED); 45 | return false; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /redis-tx-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.springboot 7 | redis-tx-demo 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | SpringBoot redis TX demo 12 | Demo project for Spring Boot with Redis transaction 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.2.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-redis 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-rest 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-jdbc 39 | 40 | 41 | com.h2database 42 | h2 43 | runtime 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/service/JwtAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.service; 2 | 3 | import java.util.Calendar; 4 | 5 | import org.springframework.security.authentication.AuthenticationProvider; 6 | import org.springframework.security.authentication.BadCredentialsException; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.core.AuthenticationException; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.web.authentication.www.NonceExpiredException; 11 | 12 | import com.auth0.jwt.JWT; 13 | import com.auth0.jwt.JWTVerifier; 14 | import com.auth0.jwt.algorithms.Algorithm; 15 | import com.auth0.jwt.interfaces.DecodedJWT; 16 | import com.github.demo.configuration.JwtAuthenticationToken; 17 | 18 | public class JwtAuthenticationProvider implements AuthenticationProvider{ 19 | 20 | private JwtUserService userService; 21 | 22 | public JwtAuthenticationProvider(JwtUserService userService) { 23 | this.userService = userService; 24 | } 25 | 26 | @Override 27 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 28 | DecodedJWT jwt = ((JwtAuthenticationToken)authentication).getToken(); 29 | if(jwt.getExpiresAt().before(Calendar.getInstance().getTime())) 30 | throw new NonceExpiredException("Token expires"); 31 | String username = jwt.getSubject(); 32 | UserDetails user = userService.getUserLoginInfo(username); 33 | if(user == null || user.getPassword()==null) 34 | throw new NonceExpiredException("Token expires"); 35 | String encryptSalt = user.getPassword(); 36 | try { 37 | Algorithm algorithm = Algorithm.HMAC256(encryptSalt); 38 | JWTVerifier verifier = JWT.require(algorithm) 39 | .withSubject(username) 40 | .build(); 41 | verifier.verify(jwt.getToken()); 42 | } catch (Exception e) { 43 | throw new BadCredentialsException("JWT token verify fail", e); 44 | } 45 | JwtAuthenticationToken token = new JwtAuthenticationToken(user, jwt, user.getAuthorities()); 46 | return token; 47 | } 48 | 49 | @Override 50 | public boolean supports(Class authentication) { 51 | return authentication.isAssignableFrom(JwtAuthenticationToken.class); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/filter/MyUsernamePasswordAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.filter; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.Charset; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.AuthenticationException; 13 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 14 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 15 | import org.springframework.util.Assert; 16 | import org.springframework.util.StreamUtils; 17 | import org.springframework.util.StringUtils; 18 | 19 | import com.alibaba.fastjson.JSON; 20 | import com.alibaba.fastjson.JSONObject; 21 | 22 | public class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { 23 | 24 | public MyUsernamePasswordAuthenticationFilter() { 25 | super(new AntPathRequestMatcher("/login", "POST")); 26 | } 27 | 28 | @Override 29 | public void afterPropertiesSet() { 30 | Assert.notNull(getAuthenticationManager(), "authenticationManager must be specified"); 31 | Assert.notNull(getSuccessHandler(), "AuthenticationSuccessHandler must be specified"); 32 | Assert.notNull(getFailureHandler(), "AuthenticationFailureHandler must be specified"); 33 | } 34 | 35 | @Override 36 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) 37 | throws AuthenticationException, IOException, ServletException { 38 | String body = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8")); 39 | String username = null, password = null; 40 | if(StringUtils.hasText(body)) { 41 | JSONObject jsonObj = JSON.parseObject(body); 42 | username = jsonObj.getString("username"); 43 | password = jsonObj.getString("password"); 44 | } 45 | 46 | if (username == null) 47 | username = ""; 48 | if (password == null) 49 | password = ""; 50 | username = username.trim(); 51 | 52 | UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( 53 | username, password); 54 | 55 | return this.getAuthenticationManager().authenticate(authRequest); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /security-jwt-demo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.service; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.apache.shiro.crypto.hash.Sha256Hash; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.redis.core.StringRedisTemplate; 10 | import org.springframework.stereotype.Service; 11 | 12 | import com.github.demo.configuration.JwtUtils; 13 | import com.github.demo.dto.UserDto; 14 | 15 | /** 16 | * 用户信息接口 17 | */ 18 | @Service 19 | public class UserService { 20 | 21 | private static final String encryptSalt = "F12839WhsnnEV$#23b"; 22 | 23 | @Autowired 24 | private StringRedisTemplate redisTemplate; 25 | 26 | /** 27 | * 保存user登录信息,返回token 28 | * @param userDto 29 | */ 30 | public String generateJwtToken(String username) { 31 | String salt = "12345";//JwtUtils.generateSalt(); 32 | /** 33 | * @todo 将salt保存到数据库或者缓存中 34 | * redisTemplate.opsForValue().set("token:"+username, salt, 3600, TimeUnit.SECONDS); 35 | */ 36 | return JwtUtils.sign(username, salt, 3600); //生成jwt token,设置过期时间为1小时 37 | } 38 | 39 | /** 40 | * 获取上次token生成时的salt值和登录用户信息 41 | * @param username 42 | * @return 43 | */ 44 | public UserDto getJwtTokenInfo(String username) { 45 | String salt = "12345"; 46 | /** 47 | * @todo 从数据库或者缓存中取出jwt token生成时用的salt 48 | * salt = redisTemplate.opsForValue().get("token:"+username); 49 | */ 50 | UserDto user = getUserInfo(username); 51 | user.setSalt(salt); 52 | return user; 53 | } 54 | 55 | /** 56 | * 清除token信息 57 | * @param userName 登录用户名 58 | * @param terminal 登录终端 59 | */ 60 | public void deleteLoginInfo(String username) { 61 | /** 62 | * @todo 删除数据库或者缓存中保存的salt 63 | * redisTemplate.delete("token:"+username); 64 | */ 65 | 66 | } 67 | 68 | /** 69 | * 获取数据库中保存的用户信息,主要是加密后的密码 70 | * @param userName 71 | * @return 72 | */ 73 | public UserDto getUserInfo(String userName) { 74 | UserDto user = new UserDto(); 75 | user.setUserId(1L); 76 | user.setUsername("admin"); 77 | user.setEncryptPwd(new Sha256Hash("123456", encryptSalt).toHex()); 78 | return user; 79 | } 80 | 81 | /** 82 | * 获取用户角色列表,强烈建议从缓存中获取 83 | * @param userId 84 | * @return 85 | */ 86 | public List getUserRoles(Long userId){ 87 | return Arrays.asList("admin"); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/JwtUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.auth0.jwt.exceptions.JWTDecodeException; 6 | import com.auth0.jwt.interfaces.DecodedJWT; 7 | import org.apache.shiro.crypto.SecureRandomNumberGenerator; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | import java.util.Calendar; 11 | import java.util.Date; 12 | 13 | public class JwtUtils { 14 | 15 | /** 16 | * 获得token中的信息无需secret解密也能获得 17 | * @return token中包含的签发时间 18 | */ 19 | public static Date getIssuedAt(String token) { 20 | try { 21 | DecodedJWT jwt = JWT.decode(token); 22 | return jwt.getIssuedAt(); 23 | } catch (JWTDecodeException e) { 24 | return null; 25 | } 26 | } 27 | 28 | /** 29 | * 获得token中的信息无需secret解密也能获得 30 | * @return token中包含的用户名 31 | */ 32 | public static String getUsername(String token) { 33 | try { 34 | DecodedJWT jwt = JWT.decode(token); 35 | return jwt.getClaim("username").asString(); 36 | } catch (JWTDecodeException e) { 37 | return null; 38 | } 39 | } 40 | 41 | /** 42 | * 生成签名,expireTime后过期 43 | * @param username 用户名 44 | * @param time 过期时间s 45 | * @return 加密的token 46 | */ 47 | public static String sign(String username, String salt, long time) { 48 | try { 49 | Date date = new Date(System.currentTimeMillis()+time*1000); 50 | Algorithm algorithm = Algorithm.HMAC256(salt); 51 | // 附带username信息 52 | return JWT.create() 53 | .withClaim("username", username) 54 | .withExpiresAt(date) 55 | .withIssuedAt(new Date()) 56 | .sign(algorithm); 57 | } catch (UnsupportedEncodingException e) { 58 | return null; 59 | } 60 | } 61 | 62 | /** 63 | * token是否过期 64 | * @return true:过期 65 | */ 66 | public static boolean isTokenExpired(String token) { 67 | Date now = Calendar.getInstance().getTime(); 68 | DecodedJWT jwt = JWT.decode(token); 69 | return jwt.getExpiresAt().before(now); 70 | } 71 | 72 | /** 73 | * 生成随机盐,长度32位 74 | * @return 75 | */ 76 | public static String generateSalt(){ 77 | SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator(); 78 | String hex = secureRandom.nextBytes(16).toHex(); 79 | return hex; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/service/JwtUserService.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.service; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.security.core.userdetails.User; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.security.crypto.bcrypt.BCrypt; 10 | import org.springframework.security.crypto.factory.PasswordEncoderFactories; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | 13 | import com.auth0.jwt.JWT; 14 | import com.auth0.jwt.algorithms.Algorithm; 15 | 16 | public class JwtUserService implements UserDetailsService{ 17 | 18 | private PasswordEncoder passwordEncoder; 19 | 20 | public JwtUserService() { 21 | this.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); //默认使用 bcrypt, strength=10 22 | } 23 | 24 | public UserDetails getUserLoginInfo(String username) { 25 | String salt = "123456ef"; 26 | /** 27 | * @todo 从数据库或者缓存中取出jwt token生成时用的salt 28 | * salt = redisTemplate.opsForValue().get("token:"+username); 29 | */ 30 | UserDetails user = loadUserByUsername(username); 31 | //将salt放到password字段返回 32 | return User.builder().username(user.getUsername()).password(salt).authorities(user.getAuthorities()).build(); 33 | } 34 | 35 | public String saveUserLoginInfo(UserDetails user) { 36 | String salt = "123456ef"; //BCrypt.gensalt(); 正式开发时可以调用该方法实时生成加密的salt 37 | /** 38 | * @todo 将salt保存到数据库或者缓存中 39 | * redisTemplate.opsForValue().set("token:"+username, salt, 3600, TimeUnit.SECONDS); 40 | */ 41 | Algorithm algorithm = Algorithm.HMAC256(salt); 42 | Date date = new Date(System.currentTimeMillis()+3600*1000); //设置1小时后过期 43 | return JWT.create() 44 | .withSubject(user.getUsername()) 45 | .withExpiresAt(date) 46 | .withIssuedAt(new Date()) 47 | .sign(algorithm); 48 | } 49 | 50 | @Override 51 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 52 | return User.builder().username("Jack").password(passwordEncoder.encode("jack-password")).roles("USER").build(); 53 | } 54 | 55 | public void createUser(String username, String password) { 56 | String encryptPwd = passwordEncoder.encode(password); 57 | /** 58 | * @todo 保存用户名和加密后密码到数据库 59 | */ 60 | } 61 | 62 | public void deleteUserLoginInfo(String username) { 63 | /** 64 | * @todo 清除数据库或者缓存中登录salt 65 | */ 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.controller; 2 | 3 | import com.github.demo.dto.UserDto; 4 | import com.github.demo.service.UserService; 5 | 6 | import org.apache.shiro.SecurityUtils; 7 | import org.apache.shiro.authc.AuthenticationException; 8 | import org.apache.shiro.authc.UsernamePasswordToken; 9 | import org.apache.shiro.subject.Subject; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.http.*; 13 | import org.springframework.web.bind.annotation.*; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | @RestController 18 | public class LoginController { 19 | 20 | private Logger logger = LoggerFactory.getLogger(LoginController.class); 21 | 22 | private UserService userService; 23 | 24 | public LoginController(UserService userService) { 25 | this.userService = userService; 26 | } 27 | 28 | /** 29 | * 用户名密码登录 30 | * @param request 31 | * @return token 32 | */ 33 | @PostMapping(value = "/login") 34 | public ResponseEntity login(@RequestBody UserDto loginInfo, HttpServletRequest request, HttpServletResponse response){ 35 | Subject subject = SecurityUtils.getSubject(); 36 | try { 37 | UsernamePasswordToken token = new UsernamePasswordToken(loginInfo.getUsername(), loginInfo.getPassword()); 38 | subject.login(token); 39 | 40 | UserDto user = (UserDto) subject.getPrincipal(); 41 | String newToken = userService.generateJwtToken(user.getUsername()); 42 | response.setHeader("x-auth-token", newToken); 43 | 44 | return ResponseEntity.ok().build(); 45 | } catch (AuthenticationException e) { 46 | logger.error("User {} login fail, Reason:{}", loginInfo.getUsername(), e.getMessage()); 47 | return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); 48 | } catch (Exception e) { 49 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); 50 | } 51 | } 52 | 53 | /** 54 | * 退出登录 55 | * @return 56 | */ 57 | @GetMapping(value = "/logout") 58 | public ResponseEntity logout() { 59 | Subject subject = SecurityUtils.getSubject(); 60 | if(subject.getPrincipals() != null) { 61 | UserDto user = (UserDto)subject.getPrincipals().getPrimaryPrincipal(); 62 | userService.deleteLoginInfo(user.getUsername()); 63 | } 64 | SecurityUtils.getSubject().logout(); 65 | return ResponseEntity.ok().build(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/DbShiroRealm.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.shiro.authc.AuthenticationException; 6 | import org.apache.shiro.authc.AuthenticationInfo; 7 | import org.apache.shiro.authc.AuthenticationToken; 8 | import org.apache.shiro.authc.SimpleAuthenticationInfo; 9 | import org.apache.shiro.authc.UsernamePasswordToken; 10 | import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 11 | import org.apache.shiro.authz.AuthorizationInfo; 12 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 13 | import org.apache.shiro.crypto.hash.Sha256Hash; 14 | import org.apache.shiro.realm.AuthorizingRealm; 15 | import org.apache.shiro.subject.PrincipalCollection; 16 | import org.apache.shiro.util.ByteSource; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import com.github.demo.dto.UserDto; 21 | import com.github.demo.service.UserService; 22 | 23 | public class DbShiroRealm extends AuthorizingRealm { 24 | private final Logger log = LoggerFactory.getLogger(DbShiroRealm.class); 25 | 26 | private static final String encryptSalt = "F12839WhsnnEV$#23b"; 27 | private UserService userService; 28 | 29 | public DbShiroRealm(UserService userService) { 30 | this.userService = userService; 31 | this.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME)); 32 | } 33 | 34 | @Override 35 | public boolean supports(AuthenticationToken token) { 36 | return token instanceof UsernamePasswordToken; 37 | } 38 | 39 | @Override 40 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 41 | UsernamePasswordToken userpasswordToken = (UsernamePasswordToken)token; 42 | String username = userpasswordToken.getUsername(); 43 | UserDto user = userService.getUserInfo(username); 44 | if(user == null) 45 | throw new AuthenticationException("用户名或者密码错误"); 46 | 47 | return new SimpleAuthenticationInfo(user, user.getEncryptPwd(), ByteSource.Util.bytes(encryptSalt), "dbRealm"); 48 | } 49 | 50 | 51 | @Override 52 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 53 | SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); 54 | UserDto user = (UserDto) principals.getPrimaryPrincipal(); 55 | List roles = user.getRoles(); 56 | if(roles == null) { 57 | roles = userService.getUserRoles(user.getUserId()); 58 | user.setRoles(roles); 59 | } 60 | if (roles != null) 61 | simpleAuthorizationInfo.addRoles(roles); 62 | 63 | return simpleAuthorizationInfo; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/dto/ArticleDto.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.dto; 2 | 3 | import java.util.Date; 4 | 5 | public class ArticleDto implements java.io.Serializable{ 6 | private static final long serialVersionUID = -2440471074054288487L; 7 | 8 | private Long id; 9 | private String title; 10 | private String author; 11 | private Date issueTime; 12 | private Date created; 13 | private Date modified; 14 | private Long createUserId; 15 | private String createUserName; 16 | private Integer status; //0:待发布, 1:已发布, 2:已删除 17 | private String content; 18 | private String headImgUrl; //标题图片 19 | 20 | public Long getId() { 21 | return id; 22 | } 23 | 24 | public void setId(Long id) { 25 | this.id = id; 26 | } 27 | 28 | public String getTitle() { 29 | return title; 30 | } 31 | 32 | public void setTitle(String title) { 33 | this.title = title; 34 | } 35 | 36 | public String getAuthor() { 37 | return author; 38 | } 39 | 40 | public void setAuthor(String auth) { 41 | this.author = auth; 42 | } 43 | 44 | public Date getCreated() { 45 | return created; 46 | } 47 | 48 | public void setCreated(Date created) { 49 | this.created = created; 50 | } 51 | 52 | public Date getModified() { 53 | return modified; 54 | } 55 | 56 | public void setModified(Date modified) { 57 | this.modified = modified; 58 | } 59 | 60 | public Integer getStatus() { 61 | return status; 62 | } 63 | 64 | public void setStatus(Integer status) { 65 | this.status = status; 66 | } 67 | 68 | public String getContent() { 69 | return content; 70 | } 71 | 72 | public void setContent(String content) { 73 | this.content = content; 74 | } 75 | 76 | public Date getIssueTime() { 77 | return issueTime; 78 | } 79 | 80 | public void setIssueTime(Date issueTime) { 81 | this.issueTime = issueTime; 82 | } 83 | 84 | public Long getCreateUserId() { 85 | return createUserId; 86 | } 87 | 88 | public void setCreateUserId(Long createUserId) { 89 | this.createUserId = createUserId; 90 | } 91 | 92 | public String getCreateUserName() { 93 | return createUserName; 94 | } 95 | 96 | public void setCreateUserName(String createUserName) { 97 | this.createUserName = createUserName; 98 | } 99 | 100 | public String getHeadImgUrl() { 101 | return headImgUrl; 102 | } 103 | 104 | public void setHeadImgUrl(String headImgUrl) { 105 | this.headImgUrl = headImgUrl; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/ResponseHeaderAdvice.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.springframework.core.MethodParameter; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.http.converter.HttpMessageConverter; 6 | import org.springframework.http.server.ServerHttpRequest; 7 | import org.springframework.http.server.ServerHttpResponse; 8 | import org.springframework.http.server.ServletServerHttpRequest; 9 | import org.springframework.http.server.ServletServerHttpResponse; 10 | import org.springframework.web.bind.annotation.ControllerAdvice; 11 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | 16 | @ControllerAdvice 17 | public class ResponseHeaderAdvice implements ResponseBodyAdvice { 18 | @Override 19 | public boolean supports(MethodParameter methodParameter, Class> aClass) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, 25 | ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { 26 | ServletServerHttpRequest serverRequest = (ServletServerHttpRequest)serverHttpRequest; 27 | ServletServerHttpResponse serverResponse = (ServletServerHttpResponse)serverHttpResponse; 28 | if(serverRequest == null || serverResponse == null 29 | || serverRequest.getServletRequest() == null || serverResponse.getServletResponse() == null) { 30 | return o; 31 | } 32 | 33 | // 对于未添加跨域消息头的响应进行处理 34 | HttpServletRequest request = serverRequest.getServletRequest(); 35 | HttpServletResponse response = serverResponse.getServletResponse(); 36 | String originHeader = "Access-Control-Allow-Origin"; 37 | if(!response.containsHeader(originHeader)) { 38 | String origin = request.getHeader("Origin"); 39 | if(origin == null) { 40 | String referer = request.getHeader("Referer"); 41 | if(referer != null) 42 | origin = referer.substring(0, referer.indexOf("/", 7)); 43 | } 44 | response.setHeader("Access-Control-Allow-Origin", origin); 45 | } 46 | 47 | String allowHeaders = "Access-Control-Allow-Headers"; 48 | if(!response.containsHeader(allowHeaders)) 49 | response.setHeader(allowHeaders, request.getHeader(allowHeaders)); 50 | 51 | String allowMethods = "Access-Control-Allow-Methods"; 52 | if(!response.containsHeader(allowMethods)) 53 | response.setHeader(allowMethods, "GET,POST,OPTIONS,HEAD"); 54 | 55 | String exposeHeaders = "access-control-expose-headers"; 56 | if(!response.containsHeader(exposeHeaders)) 57 | response.setHeader(exposeHeaders, "x-auth-token"); 58 | 59 | return o; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /shiro-jwt-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.springboot 8 | shiro-jwt-demo 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | Spring Boot with Shiro and JWT Demo 13 | Demo project for Spring Boot with Shiro and JWT 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.4.RELEASE 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 1.8 23 | 1.4.0 24 | 3.2.0 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-test 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-data-redis 39 | 40 | 41 | org.apache.shiro 42 | shiro-spring-boot-web-starter 43 | ${shiro.spring.version} 44 | 45 | 46 | com.auth0 47 | java-jwt 48 | ${jwt.auth0.version} 49 | 50 | 51 | org.apache.httpcomponents 52 | httpclient 53 | 4.5.5 54 | 55 | 56 | org.apache.commons 57 | commons-lang3 58 | 3.7 59 | 60 | 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-compiler-plugin 70 | 71 | ${java.version} 72 | ${java.version} 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-surefire-plugin 78 | 79 | true 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/configuration/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import org.apache.shiro.authc.Authenticator; 4 | import org.apache.shiro.authc.pam.FirstSuccessfulStrategy; 5 | import org.apache.shiro.authc.pam.ModularRealmAuthenticator; 6 | import org.apache.shiro.mgt.SecurityManager; 7 | import org.apache.shiro.mgt.SessionStorageEvaluator; 8 | import org.apache.shiro.realm.Realm; 9 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 10 | import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; 11 | import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; 12 | import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator; 13 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | 17 | import com.github.demo.filter.AnyRolesAuthorizationFilter; 18 | import com.github.demo.filter.JwtAuthFilter; 19 | import com.github.demo.service.UserService; 20 | 21 | import javax.servlet.DispatcherType; 22 | import javax.servlet.Filter; 23 | 24 | import java.util.Arrays; 25 | import java.util.Map; 26 | 27 | /** 28 | * shiro配置类 29 | */ 30 | @Configuration 31 | public class ShiroConfig { 32 | 33 | @Bean 34 | public FilterRegistrationBean filterRegistrationBean(SecurityManager securityManager,UserService userService) throws Exception{ 35 | FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); 36 | filterRegistration.setFilter((Filter)shiroFilter(securityManager, userService).getObject()); 37 | filterRegistration.addInitParameter("targetFilterLifecycle", "true"); 38 | filterRegistration.setAsyncSupported(true); 39 | filterRegistration.setEnabled(true); 40 | filterRegistration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ASYNC); 41 | 42 | return filterRegistration; 43 | } 44 | 45 | @Bean 46 | public Authenticator authenticator(UserService userService) { 47 | ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); 48 | authenticator.setRealms(Arrays.asList(jwtShiroRealm(userService), dbShiroRealm(userService))); 49 | authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy()); 50 | return authenticator; 51 | } 52 | 53 | @Bean 54 | protected SessionStorageEvaluator sessionStorageEvaluator(){ 55 | DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator(); 56 | sessionStorageEvaluator.setSessionStorageEnabled(false); 57 | return sessionStorageEvaluator; 58 | } 59 | 60 | @Bean("dbRealm") 61 | public Realm dbShiroRealm(UserService userService) { 62 | DbShiroRealm myShiroRealm = new DbShiroRealm(userService); 63 | return myShiroRealm; 64 | } 65 | 66 | @Bean("jwtRealm") 67 | public Realm jwtShiroRealm(UserService userService) { 68 | JWTShiroRealm myShiroRealm = new JWTShiroRealm(userService); 69 | return myShiroRealm; 70 | } 71 | 72 | /** 73 | * 设置过滤器 74 | */ 75 | @Bean("shiroFilter") 76 | public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, UserService userService) { 77 | ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); 78 | factoryBean.setSecurityManager(securityManager); 79 | Map filterMap = factoryBean.getFilters(); 80 | filterMap.put("authcToken", createAuthFilter(userService)); 81 | filterMap.put("anyRole", createRolesFilter()); 82 | factoryBean.setFilters(filterMap); 83 | factoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap()); 84 | 85 | return factoryBean; 86 | } 87 | 88 | @Bean 89 | protected ShiroFilterChainDefinition shiroFilterChainDefinition() { 90 | DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); 91 | chainDefinition.addPathDefinition("/login", "noSessionCreation,anon"); 92 | chainDefinition.addPathDefinition("/logout", "noSessionCreation,authcToken[permissive]"); 93 | chainDefinition.addPathDefinition("/image/**", "anon"); 94 | chainDefinition.addPathDefinition("/admin/**", "noSessionCreation,authcToken,anyRole[admin,manager]"); //只允许admin或manager角色的用户访问 95 | chainDefinition.addPathDefinition("/article/list", "noSessionCreation,authcToken"); 96 | chainDefinition.addPathDefinition("/article/*", "noSessionCreation,authcToken[permissive]"); 97 | chainDefinition.addPathDefinition("/**", "noSessionCreation,authcToken"); 98 | return chainDefinition; 99 | } 100 | 101 | protected JwtAuthFilter createAuthFilter(UserService userService){ 102 | return new JwtAuthFilter(userService); 103 | } 104 | 105 | protected AnyRolesAuthorizationFilter createRolesFilter(){ 106 | return new AnyRolesAuthorizationFilter(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/configuration/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.configuration; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.authentication.AuthenticationProvider; 8 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; 15 | import org.springframework.security.web.header.Header; 16 | import org.springframework.security.web.header.writers.StaticHeadersWriter; 17 | import org.springframework.web.cors.CorsConfiguration; 18 | import org.springframework.web.cors.CorsConfigurationSource; 19 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 20 | import org.springframework.web.filter.CorsFilter; 21 | 22 | import com.github.demo.filter.OptionsRequestFilter; 23 | import com.github.demo.service.JwtAuthenticationProvider; 24 | import com.github.demo.service.JwtUserService; 25 | 26 | @EnableWebSecurity 27 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ 28 | 29 | protected void configure(HttpSecurity http) throws Exception { 30 | http.authorizeRequests() 31 | .antMatchers("/image/**").permitAll() 32 | .antMatchers("/admin/**").hasAnyRole("ADMIN") 33 | .antMatchers("/article/**").hasRole("USER") 34 | .anyRequest().authenticated() 35 | .and() 36 | .csrf().disable() 37 | .formLogin().disable() 38 | .sessionManagement().disable() 39 | .cors() 40 | .and() 41 | .headers().addHeaderWriter(new StaticHeadersWriter(Arrays.asList( 42 | new Header("Access-control-Allow-Origin","*"), 43 | new Header("Access-Control-Expose-Headers","Authorization")))) 44 | .and() 45 | .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class) 46 | .apply(new JsonLoginConfigurer<>()).loginSuccessHandler(jsonLoginSuccessHandler()) 47 | .and() 48 | .apply(new JwtLoginConfigurer<>()).tokenValidSuccessHandler(jwtRefreshSuccessHandler()).permissiveRequestUrls("/logout") 49 | .and() 50 | .logout() 51 | // .logoutUrl("/logout") //默认就是"/logout" 52 | .addLogoutHandler(tokenClearLogoutHandler()) 53 | .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) 54 | .and() 55 | .sessionManagement().disable(); 56 | } 57 | 58 | @Override 59 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 60 | auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(jwtAuthenticationProvider()); 61 | } 62 | 63 | @Bean 64 | public AuthenticationManager authenticationManagerBean() throws Exception { 65 | return super.authenticationManagerBean(); 66 | } 67 | 68 | @Bean("jwtAuthenticationProvider") 69 | protected AuthenticationProvider jwtAuthenticationProvider() { 70 | return new JwtAuthenticationProvider(jwtUserService()); 71 | } 72 | 73 | @Bean("daoAuthenticationProvider") 74 | protected AuthenticationProvider daoAuthenticationProvider() throws Exception{ 75 | //这里会默认使用BCryptPasswordEncoder比对加密后的密码,注意要跟createUser时保持一致 76 | DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider(); 77 | daoProvider.setUserDetailsService(userDetailsService()); 78 | return daoProvider; 79 | } 80 | 81 | @Override 82 | protected UserDetailsService userDetailsService() { 83 | return new JwtUserService(); 84 | } 85 | 86 | @Bean("jwtUserService") 87 | protected JwtUserService jwtUserService() { 88 | return new JwtUserService(); 89 | } 90 | 91 | @Bean 92 | protected JsonLoginSuccessHandler jsonLoginSuccessHandler() { 93 | return new JsonLoginSuccessHandler(jwtUserService()); 94 | } 95 | 96 | @Bean 97 | protected JwtRefreshSuccessHandler jwtRefreshSuccessHandler() { 98 | return new JwtRefreshSuccessHandler(jwtUserService()); 99 | } 100 | 101 | @Bean 102 | protected TokenClearLogoutHandler tokenClearLogoutHandler() { 103 | return new TokenClearLogoutHandler(jwtUserService()); 104 | } 105 | 106 | @Bean 107 | protected CorsConfigurationSource corsConfigurationSource() { 108 | CorsConfiguration configuration = new CorsConfiguration(); 109 | configuration.setAllowedOrigins(Arrays.asList("*")); 110 | configuration.setAllowedMethods(Arrays.asList("GET","POST","HEAD", "OPTION")); 111 | configuration.setAllowedHeaders(Arrays.asList("*")); 112 | configuration.addExposedHeader("Authorization"); 113 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 114 | source.registerCorsConfiguration("/**", configuration); 115 | return source; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /security-jwt-demo/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /shiro-jwt-demo/src/main/java/com/github/demo/filter/JwtAuthFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.filter; 2 | 3 | import com.github.demo.configuration.JWTToken; 4 | import com.github.demo.configuration.JwtUtils; 5 | import com.github.demo.dto.UserDto; 6 | import com.github.demo.service.UserService; 7 | 8 | import org.apache.commons.lang3.BooleanUtils; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.apache.http.HttpStatus; 11 | import org.apache.shiro.authc.AuthenticationException; 12 | import org.apache.shiro.authc.AuthenticationToken; 13 | import org.apache.shiro.subject.Subject; 14 | import org.apache.shiro.web.filter.authc.AuthenticatingFilter; 15 | import org.apache.shiro.web.util.WebUtils; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.web.bind.annotation.RequestMethod; 19 | import org.springframework.web.context.request.async.WebAsyncManager; 20 | import org.springframework.web.context.request.async.WebAsyncUtils; 21 | 22 | import javax.servlet.ServletRequest; 23 | import javax.servlet.ServletResponse; 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | import java.time.LocalDateTime; 27 | import java.time.ZoneId; 28 | import java.util.Date; 29 | 30 | public class JwtAuthFilter extends AuthenticatingFilter { 31 | private final Logger log = LoggerFactory.getLogger(JwtAuthFilter.class); 32 | 33 | private static final int tokenRefreshInterval = 300; 34 | private UserService userService; 35 | 36 | public JwtAuthFilter(UserService userService){ 37 | this.userService = userService; 38 | this.setLoginUrl("/login"); 39 | } 40 | 41 | @Override 42 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 43 | HttpServletRequest httpServletRequest = WebUtils.toHttp(request); 44 | if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) //对于OPTION请求做拦截,不做token校验 45 | return false; 46 | 47 | return super.preHandle(request, response); 48 | } 49 | 50 | @Override 51 | protected void postHandle(ServletRequest request, ServletResponse response){ 52 | this.fillCorsHeader(WebUtils.toHttp(request), WebUtils.toHttp(response)); 53 | request.setAttribute("jwtShiroFilter.FILTERED", true); 54 | } 55 | 56 | @Override 57 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 58 | if(this.isLoginRequest(request, response)) 59 | return true; 60 | Boolean afterFiltered = (Boolean)(request.getAttribute("jwtShiroFilter.FILTERED")); 61 | if( BooleanUtils.isTrue(afterFiltered)) 62 | return true; 63 | 64 | boolean allowed = false; 65 | try { 66 | allowed = executeLogin(request, response); 67 | } catch(IllegalStateException e){ //not found any token 68 | log.error("Not found any token"); 69 | }catch (Exception e) { 70 | log.error("Error occurs when login", e); 71 | } 72 | return allowed || super.isPermissive(mappedValue); 73 | } 74 | 75 | @Override 76 | protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) { 77 | String jwtToken = getAuthzHeader(servletRequest); 78 | if(StringUtils.isNotBlank(jwtToken)&&!JwtUtils.isTokenExpired(jwtToken)) 79 | return new JWTToken(jwtToken); 80 | 81 | return null; 82 | } 83 | 84 | @Override 85 | protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { 86 | HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse); 87 | httpResponse.setCharacterEncoding("UTF-8"); 88 | httpResponse.setContentType("application/json;charset=UTF-8"); 89 | httpResponse.setStatus(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION); 90 | fillCorsHeader(WebUtils.toHttp(servletRequest), httpResponse); 91 | return false; 92 | } 93 | 94 | @Override 95 | protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { 96 | HttpServletResponse httpResponse = WebUtils.toHttp(response); 97 | String newToken = null; 98 | if(token instanceof JWTToken){ 99 | JWTToken jwtToken = (JWTToken)token; 100 | UserDto user = (UserDto) subject.getPrincipal(); 101 | boolean shouldRefresh = shouldTokenRefresh(JwtUtils.getIssuedAt(jwtToken.getToken())); 102 | if(shouldRefresh) { 103 | newToken = userService.generateJwtToken(user.getUsername()); 104 | } 105 | } 106 | if(StringUtils.isNotBlank(newToken)) 107 | httpResponse.setHeader("x-auth-token", newToken); 108 | 109 | return true; 110 | } 111 | 112 | @Override 113 | protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { 114 | log.error("Validate token fail, token:{}, error:{}", token.toString(), e.getMessage()); 115 | return false; 116 | } 117 | 118 | protected String getAuthzHeader(ServletRequest request) { 119 | HttpServletRequest httpRequest = WebUtils.toHttp(request); 120 | String header = httpRequest.getHeader("x-auth-token"); 121 | return StringUtils.removeStart(header, "Bearer "); 122 | } 123 | 124 | protected boolean shouldTokenRefresh(Date issueAt){ 125 | LocalDateTime issueTime = LocalDateTime.ofInstant(issueAt.toInstant(), ZoneId.systemDefault()); 126 | return LocalDateTime.now().minusSeconds(tokenRefreshInterval).isAfter(issueTime); 127 | } 128 | 129 | protected void fillCorsHeader(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){ 130 | httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); 131 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD"); 132 | httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /security-jwt-demo/src/main/java/com/github/demo/filter/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.demo.filter; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.security.authentication.AuthenticationManager; 14 | import org.springframework.security.authentication.InsufficientAuthenticationException; 15 | import org.springframework.security.authentication.InternalAuthenticationServiceException; 16 | import org.springframework.security.core.Authentication; 17 | import org.springframework.security.core.AuthenticationException; 18 | import org.springframework.security.core.context.SecurityContextHolder; 19 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 20 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 21 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 22 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 23 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 24 | import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; 25 | import org.springframework.security.web.util.matcher.RequestMatcher; 26 | import org.springframework.util.Assert; 27 | import org.springframework.web.filter.OncePerRequestFilter; 28 | 29 | import com.auth0.jwt.JWT; 30 | import com.auth0.jwt.exceptions.JWTDecodeException; 31 | import com.github.demo.configuration.JwtAuthenticationToken; 32 | 33 | public class JwtAuthenticationFilter extends OncePerRequestFilter{ 34 | 35 | private RequestMatcher requiresAuthenticationRequestMatcher; 36 | private List permissiveRequestMatchers; 37 | private AuthenticationManager authenticationManager; 38 | 39 | 40 | private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); 41 | private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); 42 | 43 | public JwtAuthenticationFilter() { 44 | this.requiresAuthenticationRequestMatcher = new RequestHeaderRequestMatcher("Authorization"); 45 | } 46 | 47 | @Override 48 | public void afterPropertiesSet() { 49 | Assert.notNull(authenticationManager, "authenticationManager must be specified"); 50 | Assert.notNull(successHandler, "AuthenticationSuccessHandler must be specified"); 51 | Assert.notNull(failureHandler, "AuthenticationFailureHandler must be specified"); 52 | } 53 | 54 | protected String getJwtToken(HttpServletRequest request) { 55 | String authInfo = request.getHeader("Authorization"); 56 | return StringUtils.removeStart(authInfo, "Bearer "); 57 | } 58 | 59 | @Override 60 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 61 | throws ServletException, IOException { 62 | if (!requiresAuthentication(request, response)) { 63 | filterChain.doFilter(request, response); 64 | return; 65 | } 66 | Authentication authResult = null; 67 | AuthenticationException failed = null; 68 | try { 69 | String token = getJwtToken(request); 70 | if(StringUtils.isNotBlank(token)) { 71 | JwtAuthenticationToken authToken = new JwtAuthenticationToken(JWT.decode(token)); 72 | authResult = this.getAuthenticationManager().authenticate(authToken); 73 | } else { 74 | failed = new InsufficientAuthenticationException("JWT is Empty"); 75 | } 76 | } catch(JWTDecodeException e) { 77 | logger.error("JWT format error", e); 78 | failed = new InsufficientAuthenticationException("JWT format error", failed); 79 | }catch (InternalAuthenticationServiceException e) { 80 | logger.error( 81 | "An internal error occurred while trying to authenticate the user.", 82 | failed); 83 | failed = e; 84 | }catch (AuthenticationException e) { 85 | // Authentication failed 86 | failed = e; 87 | } 88 | if(authResult != null) { 89 | successfulAuthentication(request, response, filterChain, authResult); 90 | } else if(!permissiveRequest(request)){ 91 | unsuccessfulAuthentication(request, response, failed); 92 | return; 93 | } 94 | 95 | filterChain.doFilter(request, response); 96 | } 97 | 98 | protected void unsuccessfulAuthentication(HttpServletRequest request, 99 | HttpServletResponse response, AuthenticationException failed) 100 | throws IOException, ServletException { 101 | SecurityContextHolder.clearContext(); 102 | failureHandler.onAuthenticationFailure(request, response, failed); 103 | } 104 | 105 | protected void successfulAuthentication(HttpServletRequest request, 106 | HttpServletResponse response, FilterChain chain, Authentication authResult) 107 | throws IOException, ServletException{ 108 | SecurityContextHolder.getContext().setAuthentication(authResult); 109 | successHandler.onAuthenticationSuccess(request, response, authResult); 110 | } 111 | 112 | protected AuthenticationManager getAuthenticationManager() { 113 | return authenticationManager; 114 | } 115 | 116 | public void setAuthenticationManager(AuthenticationManager authenticationManager) { 117 | this.authenticationManager = authenticationManager; 118 | } 119 | 120 | protected boolean requiresAuthentication(HttpServletRequest request, 121 | HttpServletResponse response) { 122 | return requiresAuthenticationRequestMatcher.matches(request); 123 | } 124 | 125 | protected boolean permissiveRequest(HttpServletRequest request) { 126 | if(permissiveRequestMatchers == null) 127 | return false; 128 | for(RequestMatcher permissiveMatcher : permissiveRequestMatchers) { 129 | if(permissiveMatcher.matches(request)) 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | public void setPermissiveUrl(String... urls) { 136 | if(permissiveRequestMatchers == null) 137 | permissiveRequestMatchers = new ArrayList<>(); 138 | for(String url : urls) 139 | permissiveRequestMatchers .add(new AntPathRequestMatcher(url)); 140 | } 141 | 142 | public void setAuthenticationSuccessHandler( 143 | AuthenticationSuccessHandler successHandler) { 144 | Assert.notNull(successHandler, "successHandler cannot be null"); 145 | this.successHandler = successHandler; 146 | } 147 | 148 | public void setAuthenticationFailureHandler( 149 | AuthenticationFailureHandler failureHandler) { 150 | Assert.notNull(failureHandler, "failureHandler cannot be null"); 151 | this.failureHandler = failureHandler; 152 | } 153 | 154 | protected AuthenticationSuccessHandler getSuccessHandler() { 155 | return successHandler; 156 | } 157 | 158 | protected AuthenticationFailureHandler getFailureHandler() { 159 | return failureHandler; 160 | } 161 | 162 | } 163 | --------------------------------------------------------------------------------