├── shiroJWT ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── mapper │ │ │ │ └── userMapper.xml │ │ └── java │ │ │ └── com │ │ │ └── howie │ │ │ └── shirojwt │ │ │ ├── ShiroJwtApplication.java │ │ │ ├── shiro │ │ │ ├── JWTToken.java │ │ │ └── CustomRealm.java │ │ │ ├── model │ │ │ └── ResultMap.java │ │ │ ├── controller │ │ │ ├── GuestController.java │ │ │ ├── AdminController.java │ │ │ ├── ExceptionController.java │ │ │ ├── LoginController.java │ │ │ └── UserController.java │ │ │ ├── mapper │ │ │ └── UserMapper.java │ │ │ ├── util │ │ │ └── JWTUtil.java │ │ │ ├── config │ │ │ └── ShiroConfig.java │ │ │ └── filter │ │ │ └── JWTFilter.java │ └── test │ │ └── java │ │ └── com │ │ └── howie │ │ └── shirojwt │ │ └── ShiroJwtApplicationTests.java ├── .gitignore ├── pom.xml ├── mvnw.cmd └── mvnw ├── shiroSimple ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── mapper │ │ │ │ └── userMapper.xml │ │ └── java │ │ │ └── com │ │ │ └── howie │ │ │ └── shiro │ │ │ ├── ShiroApplication.java │ │ │ ├── mapper │ │ │ └── UserMapper.java │ │ │ ├── model │ │ │ └── ResultMap.java │ │ │ ├── controller │ │ │ ├── UserController.java │ │ │ ├── AdminController.java │ │ │ ├── ExceptionController.java │ │ │ ├── GuestController.java │ │ │ └── LoginController.java │ │ │ ├── shiro │ │ │ └── CustomRealm.java │ │ │ └── config │ │ │ └── ShiroConfig.java │ └── test │ │ └── java │ │ └── com │ │ └── howie │ │ └── shiro │ │ └── ShiroApplicationTests.java ├── .gitignore ├── pom.xml ├── mvnw.cmd └── mvnw ├── README.md ├── shiro + springBoot 整合 JWT.md └── shiro 整合 springBoot 实现基本的角色权限控制.md /shiroJWT/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/Shiro-SpringBoot/HEAD/shiroJWT/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /shiroSimple/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/Shiro-SpringBoot/HEAD/shiroSimple/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /shiroJWT/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /shiroJWT/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/Shiro-SpringBoot/HEAD/shiroJWT/src/main/resources/application.properties -------------------------------------------------------------------------------- /shiroSimple/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /shiroSimple/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/Shiro-SpringBoot/HEAD/shiroSimple/src/main/resources/application.properties -------------------------------------------------------------------------------- /shiroJWT/.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/ -------------------------------------------------------------------------------- /shiroSimple/.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/ -------------------------------------------------------------------------------- /shiroSimple/src/test/java/com/howie/shiro/ShiroApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro; 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 ShiroApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /shiroJWT/src/test/java/com/howie/shirojwt/ShiroJwtApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt; 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 ShiroJwtApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [一. shiro 整合 springBoot 实现基本的角色权限控制](https://github.com/HowieYuan/Shiro-SpringBoot/blob/master/shiro%20%E6%95%B4%E5%90%88%20springBoot%20%E5%AE%9E%E7%8E%B0%E5%9F%BA%E6%9C%AC%E7%9A%84%E8%A7%92%E8%89%B2%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6.md) 2 | https://github.com/HowieYuan/shiro/tree/master/shiroSimple 3 | # [二. shiro + springBoot 整合 JWT](https://github.com/HowieYuan/Shiro-SpringBoot/blob/master/shiro%20%2B%20springBoot%20%E6%95%B4%E5%90%88%20JWT.md) 4 | https://github.com/HowieYuan/shiro/tree/master/shiroJWT 5 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/ShiroApplication.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @MapperScan(value = "com.howie.shiro.mapper") 9 | public class ShiroApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ShiroApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/ShiroJwtApplication.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @MapperScan(value = "com.howie.shirojwt.mapper") 9 | public class ShiroJwtApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ShiroJwtApplication.class, args); 13 | } 14 | } -------------------------------------------------------------------------------- /shiroSimple/src/main/resources/mapper/userMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.mapper; 2 | 3 | import org.springframework.stereotype.Repository; 4 | 5 | /** 6 | * Created with IntelliJ IDEA 7 | * 8 | * @Author yuanhaoyue swithaoy@gmail.com 9 | * @Description 10 | * @Date 2018-03-25 11 | * @Time 22:04 12 | */ 13 | @Repository 14 | public interface UserMapper { 15 | /** 16 | * 获得密码 17 | * @param username 用户名 18 | */ 19 | String getPassword(String username); 20 | 21 | /** 22 | * 获得角色权限 23 | * @param username 用户名 24 | * @return user/admin 25 | */ 26 | String getRole(String username); 27 | } 28 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/shiro/JWTToken.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.shiro; 2 | 3 | import org.apache.shiro.authc.AuthenticationToken; 4 | 5 | /** 6 | * Created with IntelliJ IDEA 7 | * 8 | * @Author yuanhaoyue swithaoy@gmail.com 9 | * @Description token 10 | * @Date 2018-04-09 11 | * @Time 16:54 12 | */ 13 | public class JWTToken implements AuthenticationToken { 14 | private String token; 15 | 16 | public JWTToken(String token) { 17 | this.token = token; 18 | } 19 | 20 | @Override 21 | public Object getPrincipal() { 22 | return token; 23 | } 24 | 25 | @Override 26 | public Object getCredentials() { 27 | return token; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/model/ResultMap.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.model; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description 接口返回对象 12 | * @Date 2018-03-17 13 | * @Time 22:25 14 | */ 15 | @Component 16 | public class ResultMap extends HashMap { 17 | public ResultMap() { 18 | } 19 | 20 | public ResultMap success() { 21 | this.put("result", "success"); 22 | return this; 23 | } 24 | 25 | public ResultMap fail() { 26 | this.put("result", "fail"); 27 | return this; 28 | } 29 | 30 | public ResultMap message(Object message) { 31 | this.put("message", message); 32 | return this; 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/model/ResultMap.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.model; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description 接口返回对象 12 | * @Date 2018-03-17 13 | * @Time 22:25 14 | */ 15 | @Component 16 | public class ResultMap extends HashMap { 17 | public ResultMap() { 18 | } 19 | 20 | public ResultMap success() { 21 | this.put("result", "success"); 22 | return this; 23 | } 24 | 25 | public ResultMap fail() { 26 | this.put("result", "fail"); 27 | return this; 28 | } 29 | 30 | public ResultMap code(int code) { 31 | this.put("code", code); 32 | return this; 33 | } 34 | 35 | public ResultMap message(Object message) { 36 | this.put("message", message); 37 | return this; 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/controller/GuestController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.controller; 2 | 3 | import com.howie.shirojwt.mapper.UserMapper; 4 | import com.howie.shirojwt.model.ResultMap; 5 | import com.howie.shirojwt.util.JWTUtil; 6 | import org.apache.shiro.authz.annotation.RequiresPermissions; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | /** 11 | * Created with IntelliJ IDEA 12 | * 13 | * @Author yuanhaoyue swithaoy@gmail.com 14 | * @Description 游客角色可以访问的页面 15 | * @Date 2018-04-30 16 | * @Time 14:24 17 | */ 18 | @RestController 19 | @RequestMapping("/guest") 20 | public class GuestController { 21 | private final ResultMap resultMap; 22 | 23 | @Autowired 24 | public GuestController(ResultMap resultMap) { 25 | this.resultMap = resultMap; 26 | } 27 | 28 | @GetMapping("/welcome") 29 | public ResultMap login() { 30 | return resultMap.success().code(200).message("欢迎访问游客页面!"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.controller; 2 | 3 | import com.howie.shiro.model.ResultMap; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | /** 10 | * Created with IntelliJ IDEA 11 | * 12 | * @Author yuanhaoyue swithaoy@gmail.com 13 | * @Description 权限:用户 14 | * @Date 2018-04-06 15 | * @Time 20:33 16 | */ 17 | @RestController 18 | @RequestMapping("/user") 19 | public class UserController { 20 | private final ResultMap resultMap; 21 | 22 | @Autowired 23 | public UserController(ResultMap resultMap) { 24 | this.resultMap = resultMap; 25 | } 26 | 27 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 28 | public ResultMap getMessage() { 29 | return resultMap.success().message("您拥有用户权限,可以获得该接口的信息!"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.controller; 2 | 3 | import com.howie.shiro.model.ResultMap; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | /** 10 | * Created with IntelliJ IDEA 11 | * 12 | * @Author yuanhaoyue swithaoy@gmail.com 13 | * @Description 权限:管理员 14 | * @Date 2018-04-06 15 | * @Time 20:31 16 | */ 17 | @RestController 18 | @RequestMapping("/admin") 19 | public class AdminController { 20 | private final ResultMap resultMap; 21 | 22 | @Autowired 23 | public AdminController(ResultMap resultMap) { 24 | this.resultMap = resultMap; 25 | } 26 | 27 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 28 | public ResultMap getMessage() { 29 | return resultMap.success().message("您拥有管理员权限,可以获得该接口的信息!"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/controller/ExceptionController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.controller; 2 | 3 | import com.howie.shiro.model.ResultMap; 4 | import org.apache.shiro.ShiroException; 5 | import org.apache.shiro.authc.AccountException; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | /** 11 | * Created with IntelliJ IDEA 12 | * 13 | * @Author yuanhaoyue swithaoy@gmail.com 14 | * @Description 异常处理 controller 15 | * @Date 2018-05-02 16 | * @Time 20:40 17 | */ 18 | @RestControllerAdvice 19 | public class ExceptionController { 20 | private final ResultMap resultMap; 21 | 22 | @Autowired 23 | public ExceptionController(ResultMap resultMap) { 24 | this.resultMap = resultMap; 25 | } 26 | 27 | /** 28 | * 捕捉 CustomRealm 抛出的异常 29 | */ 30 | @ExceptionHandler(AccountException.class) 31 | public ResultMap handleShiroException(Exception ex) { 32 | return resultMap.fail().message(ex.getMessage()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/controller/GuestController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.controller; 2 | 3 | import com.howie.shiro.model.ResultMap; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | /** 10 | * Created with IntelliJ IDEA 11 | * 12 | * @Author yuanhaoyue swithaoy@gmail.com 13 | * @Description 权限:游客 14 | * @Date 2018-04-06 15 | * @Time 17:19 16 | */ 17 | @RestController 18 | @RequestMapping("/guest") 19 | public class GuestController { 20 | private final ResultMap resultMap; 21 | 22 | @Autowired 23 | public GuestController(ResultMap resultMap) { 24 | this.resultMap = resultMap; 25 | } 26 | 27 | @RequestMapping(value = "/enter", method = RequestMethod.GET) 28 | public ResultMap login() { 29 | return resultMap.success().message("欢迎进入,您的身份是游客"); 30 | } 31 | 32 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 33 | public ResultMap submitLogin() { 34 | return resultMap.success().message("您拥有获得该接口的信息的权限!"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.mapper; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created with IntelliJ IDEA 10 | * 11 | * @Author yuanhaoyue swithaoy@gmail.com 12 | * @Description 13 | * @Date 2018-03-25 14 | * @Time 22:04 15 | */ 16 | @Repository 17 | public interface UserMapper { 18 | /** 19 | * 获得密码 20 | * @param username 用户名 21 | */ 22 | String getPassword(String username); 23 | 24 | /** 25 | * 获得角色权限 26 | * @param username 用户名 27 | * @return user/admin 28 | */ 29 | String getRole(String username); 30 | 31 | /** 32 | * 修改密码 33 | */ 34 | void updatePassword(@Param("username") String username, @Param("newPassword") String newPassword); 35 | 36 | /** 37 | * 获得存在的用户 38 | */ 39 | List getUser(); 40 | 41 | /** 42 | * 封号 43 | */ 44 | void banUser(String username); 45 | 46 | /** 47 | * 检查用户状态 48 | */ 49 | int checkUserBanStatus(String username); 50 | 51 | /** 52 | * 获得用户角色默认的权限 53 | */ 54 | String getRolePermission(String username); 55 | 56 | /** 57 | * 获得用户的权限 58 | */ 59 | String getPermission(String username); 60 | } 61 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.controller; 2 | 3 | import com.howie.shirojwt.mapper.UserMapper; 4 | import com.howie.shirojwt.model.ResultMap; 5 | import org.apache.shiro.authz.annotation.RequiresRoles; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Created with IntelliJ IDEA 16 | * 17 | * @Author yuanhaoyue swithaoy@gmail.com 18 | * @Description admin角色权限controller 19 | * @Date 2018-04-29 20 | * @Time 17:32 21 | */ 22 | @RestController 23 | @RequestMapping("/admin") 24 | public class AdminController { 25 | private final UserMapper userMapper; 26 | private final ResultMap resultMap; 27 | 28 | @Autowired 29 | public AdminController(UserMapper userMapper, ResultMap resultMap) { 30 | this.userMapper = userMapper; 31 | this.resultMap = resultMap; 32 | } 33 | 34 | @GetMapping("/getUser") 35 | @RequiresRoles("admin") 36 | public ResultMap getUser() { 37 | List list = userMapper.getUser(); 38 | return resultMap.success().code(200).message(list); 39 | } 40 | 41 | /** 42 | * 封号操作 43 | */ 44 | @PostMapping("/banUser") 45 | @RequiresRoles("admin") 46 | public ResultMap updatePassword(String username) { 47 | userMapper.banUser(username); 48 | return resultMap.success().code(200).message("成功封号!"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /shiroJWT/src/main/resources/mapper/userMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 15 | 16 | 17 | UPDATE user 18 | SET password = #{password} 19 | WHERE username = #{username} 20 | 21 | 22 | 26 | 27 | 28 | UPDATE user 29 | SET ban = 1 30 | WHERE username = #{username} 31 | 32 | 33 | 38 | 39 | 44 | 45 | 50 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/controller/ExceptionController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.controller; 2 | 3 | import com.howie.shirojwt.model.ResultMap; 4 | import org.apache.shiro.ShiroException; 5 | import org.apache.shiro.authz.UnauthorizedException; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.ResponseStatus; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | /** 15 | * Created with IntelliJ IDEA 16 | * 17 | * @Author yuanhaoyue swithaoy@gmail.com 18 | * @Description 异常处理 19 | * @Date 2018-04-09 20 | * @Time 17:09 21 | */ 22 | @RestControllerAdvice 23 | public class ExceptionController { 24 | private final ResultMap resultMap; 25 | 26 | @Autowired 27 | public ExceptionController(ResultMap resultMap) { 28 | this.resultMap = resultMap; 29 | } 30 | 31 | // 捕捉shiro的异常 32 | @ExceptionHandler(ShiroException.class) 33 | public ResultMap handle401() { 34 | return resultMap.fail().code(401).message("您没有权限访问!"); 35 | } 36 | 37 | // 捕捉其他所有异常 38 | @ExceptionHandler(Exception.class) 39 | public ResultMap globalException(HttpServletRequest request, Throwable ex) { 40 | return resultMap.fail() 41 | .code(getStatus(request).value()) 42 | .message("访问出错,无法访问: " + ex.getMessage()); 43 | } 44 | 45 | private HttpStatus getStatus(HttpServletRequest request) { 46 | Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); 47 | if (statusCode == null) { 48 | return HttpStatus.INTERNAL_SERVER_ERROR; 49 | } 50 | return HttpStatus.valueOf(statusCode); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.controller; 2 | 3 | import com.howie.shirojwt.mapper.UserMapper; 4 | import com.howie.shirojwt.model.ResultMap; 5 | import com.howie.shirojwt.util.JWTUtil; 6 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 7 | import org.apache.shiro.authz.annotation.RequiresRoles; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.servlet.ServletRequest; 13 | import javax.servlet.ServletResponse; 14 | import java.io.UnsupportedEncodingException; 15 | 16 | /** 17 | * Created with IntelliJ IDEA 18 | * 19 | * @Author yuanhaoyue swithaoy@gmail.com 20 | * @Description 21 | * @Date 2018-04-29 22 | * @Time 13:20 23 | */ 24 | @RestController 25 | public class LoginController { 26 | private final UserMapper userMapper; 27 | private final ResultMap resultMap; 28 | 29 | @Autowired 30 | public LoginController(UserMapper userMapper, ResultMap resultMap) { 31 | this.userMapper = userMapper; 32 | this.resultMap = resultMap; 33 | } 34 | 35 | @PostMapping("/login") 36 | public ResultMap login(@RequestParam("username") String username, 37 | @RequestParam("password") String password) { 38 | String realPassword = userMapper.getPassword(username); 39 | if (realPassword == null) { 40 | return resultMap.fail().code(401).message("用户名错误"); 41 | } else if (!realPassword.equals(password)) { 42 | return resultMap.fail().code(401).message("密码错误"); 43 | } else { 44 | return resultMap.success().code(200).message(JWTUtil.createToken(username)); 45 | } 46 | } 47 | 48 | @RequestMapping(path = "/unauthorized/{message}") 49 | public ResultMap unauthorized(@PathVariable String message) throws UnsupportedEncodingException { 50 | return resultMap.success().code(401).message(message); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /shiroSimple/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.howie 7 | shiro 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | shiro 12 | shiro 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.6.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-web 31 | 32 | 33 | org.mybatis.spring.boot 34 | mybatis-spring-boot-starter 35 | 1.3.2 36 | 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | runtime 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | 50 | org.apache.shiro 51 | shiro-spring 52 | 1.3.2 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /shiroJWT/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.howie 7 | shiro-jwt 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | shiro-jwt 12 | shiro-JWT 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.6.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-web 31 | 32 | 33 | org.mybatis.spring.boot 34 | mybatis-spring-boot-starter 35 | 1.3.2 36 | 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | runtime 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | 50 | org.apache.shiro 51 | shiro-spring 52 | 1.3.2 53 | 54 | 55 | com.auth0 56 | java-jwt 57 | 3.2.0 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.controller; 2 | 3 | import com.howie.shirojwt.mapper.UserMapper; 4 | import com.howie.shirojwt.model.ResultMap; 5 | import com.howie.shirojwt.util.JWTUtil; 6 | import org.apache.shiro.authz.annotation.Logical; 7 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 8 | import org.apache.shiro.authz.annotation.RequiresPermissions; 9 | import org.apache.shiro.authz.annotation.RequiresRoles; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | /** 15 | * Created with IntelliJ IDEA 16 | * 17 | * @Author yuanhaoyue swithaoy@gmail.com 18 | * @Description user角色权限controller 19 | * @Date 2018-04-09 20 | * @Time 17:12 21 | */ 22 | @RestController 23 | @RequestMapping("/user") 24 | public class UserController { 25 | private final UserMapper userMapper; 26 | private final ResultMap resultMap; 27 | 28 | @Autowired 29 | public UserController(UserMapper userMapper, ResultMap resultMap) { 30 | this.userMapper = userMapper; 31 | this.resultMap = resultMap; 32 | } 33 | 34 | /** 35 | * 拥有 user, admin 角色的用户可以访问下面的页面 36 | */ 37 | @GetMapping("/getMessage") 38 | @RequiresRoles(logical = Logical.OR, value = {"user", "admin"}) 39 | public ResultMap getMessage() { 40 | return resultMap.success().code(200).message("成功获得信息!"); 41 | } 42 | 43 | @PostMapping("/updatePassword") 44 | @RequiresRoles(logical = Logical.OR, value = {"user", "admin"}) 45 | public ResultMap updatePassword(String username, String oldPassword, String newPassword) { 46 | String dataBasePassword = userMapper.getPassword(username); 47 | if (dataBasePassword.equals(oldPassword)) { 48 | userMapper.updatePassword(username, newPassword); 49 | } else { 50 | return resultMap.fail().message("密码错误!"); 51 | } 52 | return resultMap.success().code(200).message("成功获得信息!"); 53 | } 54 | 55 | /** 56 | * 拥有 vip 权限可以访问该页面 57 | */ 58 | @GetMapping("/getVipMessage") 59 | @RequiresRoles(logical = Logical.OR, value = {"user", "admin"}) 60 | @RequiresPermissions("vip") 61 | public ResultMap getVipMessage() { 62 | return resultMap.success().code(200).message("成功获得 vip 信息!"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/util/JWTUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.util; 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.JWTDecodeException; 7 | import com.auth0.jwt.interfaces.DecodedJWT; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | import java.util.Date; 11 | 12 | /** 13 | * Created with IntelliJ IDEA 14 | * 15 | * @Author yuanhaoyue swithaoy@gmail.com 16 | * @Description JWT 工具类 17 | * @Date 2018-04-07 18 | * @Time 22:48 19 | */ 20 | public class JWTUtil { 21 | // 过期时间 24 小时 22 | private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000; 23 | // 密钥 24 | private static final String SECRET = "SHIRO+JWT"; 25 | 26 | /** 27 | * 生成 token, 5min后过期 28 | * 29 | * @param username 用户名 30 | * @return 加密的token 31 | */ 32 | public static String createToken(String username) { 33 | try { 34 | Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); 35 | Algorithm algorithm = Algorithm.HMAC256(SECRET); 36 | // 附带username信息 37 | return JWT.create() 38 | .withClaim("username", username) 39 | //到期时间 40 | .withExpiresAt(date) 41 | //创建一个新的JWT,并使用给定的算法进行标记 42 | .sign(algorithm); 43 | } catch (UnsupportedEncodingException e) { 44 | return null; 45 | } 46 | } 47 | 48 | /** 49 | * 校验 token 是否正确 50 | * 51 | * @param token 密钥 52 | * @param username 用户名 53 | * @return 是否正确 54 | */ 55 | public static boolean verify(String token, String username) { 56 | try { 57 | Algorithm algorithm = Algorithm.HMAC256(SECRET); 58 | //在token中附带了username信息 59 | JWTVerifier verifier = JWT.require(algorithm) 60 | .withClaim("username", username) 61 | .build(); 62 | //验证 token 63 | verifier.verify(token); 64 | return true; 65 | } catch (Exception exception) { 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * 获得token中的信息,无需secret解密也能获得 72 | * 73 | * @return token中包含的用户名 74 | */ 75 | public static String getUsername(String token) { 76 | try { 77 | DecodedJWT jwt = JWT.decode(token); 78 | return jwt.getClaim("username").asString(); 79 | } catch (JWTDecodeException e) { 80 | return null; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.controller; 2 | 3 | import com.howie.shiro.mapper.UserMapper; 4 | import com.howie.shiro.model.ResultMap; 5 | import org.apache.shiro.SecurityUtils; 6 | import org.apache.shiro.authc.UsernamePasswordToken; 7 | import org.apache.shiro.subject.Subject; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | /** 14 | * Created with IntelliJ IDEA 15 | * 16 | * @Author yuanhaoyue swithaoy@gmail.com 17 | * @Description 登陆 Controller 18 | * @Date 2018-04-03 19 | * @Time 22:28 20 | */ 21 | @RestController 22 | public class LoginController { 23 | private final ResultMap resultMap; 24 | private final UserMapper userMapper; 25 | 26 | @Autowired 27 | public LoginController(ResultMap resultMap, UserMapper userMapper) { 28 | this.resultMap = resultMap; 29 | this.userMapper = userMapper; 30 | } 31 | 32 | @RequestMapping(value = "/notLogin", method = RequestMethod.GET) 33 | public ResultMap notLogin() { 34 | return resultMap.success().message("您尚未登陆!"); 35 | } 36 | 37 | @RequestMapping(value = "/notRole", method = RequestMethod.GET) 38 | public ResultMap notRole() { 39 | return resultMap.success().message("您没有权限!"); 40 | } 41 | 42 | @RequestMapping(value = "/logout", method = RequestMethod.GET) 43 | public ResultMap logout() { 44 | Subject subject = SecurityUtils.getSubject(); 45 | subject.logout(); 46 | return resultMap.success().message("成功注销!"); 47 | } 48 | 49 | /** 50 | * 登陆 51 | * 52 | * @param username 用户名 53 | * @param password 密码 54 | */ 55 | @RequestMapping(value = "/login", method = RequestMethod.POST) 56 | public ResultMap login(String username, String password) { 57 | // 从SecurityUtils里边创建一个 subject 58 | Subject subject = SecurityUtils.getSubject(); 59 | // 在认证提交前准备 token(令牌) 60 | UsernamePasswordToken token = new UsernamePasswordToken(username, password); 61 | // 执行认证登陆 62 | subject.login(token); 63 | //根据权限,指定返回数据 64 | String role = userMapper.getRole(username); 65 | if ("user".equals(role)) { 66 | return resultMap.success().message("欢迎登陆"); 67 | } 68 | if ("admin".equals(role)) { 69 | return resultMap.success().message("欢迎来到管理员页面"); 70 | } 71 | return resultMap.fail().message("权限错误!"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/shiro/CustomRealm.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.shiro; 2 | 3 | import com.howie.shiro.mapper.UserMapper; 4 | import org.apache.shiro.SecurityUtils; 5 | import org.apache.shiro.authc.*; 6 | import org.apache.shiro.authz.AuthorizationInfo; 7 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 8 | import org.apache.shiro.realm.AuthorizingRealm; 9 | import org.apache.shiro.subject.PrincipalCollection; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | /** 17 | * Created with IntelliJ IDEA 18 | * 19 | * @Author yuanhaoyue swithaoy@gmail.com 20 | * @Description 自定义 Realm 21 | * @Date 2018-03-25 22 | * @Time 21:46 23 | */ 24 | @Component 25 | public class CustomRealm extends AuthorizingRealm { 26 | private final UserMapper userMapper; 27 | 28 | @Autowired 29 | public CustomRealm(UserMapper userMapper) { 30 | this.userMapper = userMapper; 31 | } 32 | 33 | /** 34 | * 获取身份验证信息 35 | * Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。 36 | * 37 | * @param authenticationToken 用户身份信息 token 38 | * @return 返回封装了用户信息的 AuthenticationInfo 实例 39 | */ 40 | @Override 41 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 42 | System.out.println("————身份认证方法————"); 43 | UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; 44 | // 从数据库获取对应用户名密码的用户 45 | String password = userMapper.getPassword(token.getUsername()); 46 | if (null == password) { 47 | throw new AccountException("用户名不正确"); 48 | } else if (!password.equals(new String((char[]) token.getCredentials()))) { 49 | throw new AccountException("密码不正确"); 50 | } 51 | return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName()); 52 | } 53 | 54 | /** 55 | * 获取授权信息 56 | * 57 | * @param principalCollection 58 | * @return 59 | */ 60 | @Override 61 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 62 | System.out.println("————权限认证————"); 63 | String username = (String) SecurityUtils.getSubject().getPrincipal(); 64 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 65 | //获得该用户角色 66 | String role = userMapper.getRole(username); 67 | Set set = new HashSet<>(); 68 | //需要将 role 封装到 Set 作为 info.setRoles() 的参数 69 | set.add(role); 70 | //设置该用户拥有的角色 71 | info.setRoles(set); 72 | return info; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /shiroSimple/src/main/java/com/howie/shiro/config/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package com.howie.shiro.config; 2 | 3 | import com.howie.shiro.shiro.CustomRealm; 4 | import org.apache.shiro.mgt.SecurityManager; 5 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 6 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import java.util.LinkedHashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * Created with IntelliJ IDEA 15 | * 16 | * @Author yuanhaoyue swithaoy@gmail.com 17 | * @Description shiro 配置 18 | * @Date 2018-03-28 19 | * @Time 17:21 20 | */ 21 | @Configuration 22 | public class ShiroConfig { 23 | /** 24 | * 过滤器默认权限表 {anon=anon, authc=authc, authcBasic=authcBasic, logout=logout, 25 | * noSessionCreation=noSessionCreation, perms=perms, port=port, 26 | * rest=rest, roles=roles, ssl=ssl, user=user} 27 | *

28 | * anon, authc, authcBasic, user 是第一组认证过滤器 29 | * perms, port, rest, roles, ssl 是第二组授权过滤器 30 | *

31 | * user 和 authc 的不同:当应用开启了rememberMe时, 用户下次访问时可以是一个user, 但绝不会是authc, 32 | * 因为authc是需要重新认证的, user表示用户不一定已通过认证, 只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe 33 | * 以前的一个用户登录时开启了rememberMe, 然后他关闭浏览器, 下次再访问时他就是一个user, 而不会authc 34 | * 35 | * @param securityManager 初始化 ShiroFilterFactoryBean 的时候需要注入 SecurityManager 36 | */ 37 | @Bean 38 | public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { 39 | ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 40 | // 必须设置 SecurityManager 41 | shiroFilterFactoryBean.setSecurityManager(securityManager); 42 | // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射 43 | shiroFilterFactoryBean.setLoginUrl("/notLogin"); 44 | // 设置无权限时跳转的 url; 45 | shiroFilterFactoryBean.setUnauthorizedUrl("/notRole"); 46 | 47 | // 设置拦截器 48 | Map filterChainDefinitionMap = new LinkedHashMap<>(); 49 | //游客,开发权限 50 | filterChainDefinitionMap.put("/guest/**", "anon"); 51 | //用户,需要角色权限 “user” 52 | filterChainDefinitionMap.put("/user/**", "roles[user]"); 53 | //管理员,需要角色权限 “admin” 54 | filterChainDefinitionMap.put("/admin/**", "roles[admin]"); 55 | //开放登陆接口 56 | filterChainDefinitionMap.put("/login", "anon"); 57 | //其余接口一律拦截 58 | //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 59 | filterChainDefinitionMap.put("/**", "authc"); 60 | 61 | shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 62 | System.out.println("Shiro拦截器工厂类注入成功"); 63 | return shiroFilterFactoryBean; 64 | } 65 | 66 | /** 67 | * 注入 securityManager 68 | */ 69 | @Bean 70 | public SecurityManager securityManager(CustomRealm customRealm) { 71 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 72 | // 设置realm. 73 | securityManager.setRealm(customRealm); 74 | return securityManager; 75 | } 76 | } -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/shiro/CustomRealm.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.shiro; 2 | 3 | import com.howie.shirojwt.mapper.UserMapper; 4 | import com.howie.shirojwt.util.JWTUtil; 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.authz.AuthorizationInfo; 10 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 11 | import org.apache.shiro.realm.AuthorizingRealm; 12 | import org.apache.shiro.subject.PrincipalCollection; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.util.HashSet; 17 | import java.util.Set; 18 | 19 | /** 20 | * Created with IntelliJ IDEA 21 | * 22 | * @Author yuanhaoyue swithaoy@gmail.com 23 | * @Description 自定义 Realm 24 | * @Date 2018-04-09 25 | * @Time 16:58 26 | */ 27 | @Component 28 | public class CustomRealm extends AuthorizingRealm { 29 | private final UserMapper userMapper; 30 | 31 | @Autowired 32 | public CustomRealm(UserMapper userMapper) { 33 | this.userMapper = userMapper; 34 | } 35 | 36 | /** 37 | * 必须重写此方法,不然会报错 38 | */ 39 | @Override 40 | public boolean supports(AuthenticationToken token) { 41 | return token instanceof JWTToken; 42 | } 43 | 44 | /** 45 | * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。 46 | */ 47 | @Override 48 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 49 | System.out.println("————身份认证方法————"); 50 | String token = (String) authenticationToken.getCredentials(); 51 | // 解密获得username,用于和数据库进行对比 52 | String username = JWTUtil.getUsername(token); 53 | if (username == null || !JWTUtil.verify(token, username)) { 54 | throw new AuthenticationException("token认证失败!"); 55 | } 56 | String password = userMapper.getPassword(username); 57 | if (password == null) { 58 | throw new AuthenticationException("该用户不存在!"); 59 | } 60 | int ban = userMapper.checkUserBanStatus(username); 61 | if (ban == 1) { 62 | throw new AuthenticationException("该用户已被封号!"); 63 | } 64 | return new SimpleAuthenticationInfo(token, token, "MyRealm"); 65 | } 66 | 67 | /** 68 | * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的 69 | */ 70 | @Override 71 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 72 | System.out.println("————权限认证————"); 73 | String username = JWTUtil.getUsername(principals.toString()); 74 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 75 | //获得该用户角色 76 | String role = userMapper.getRole(username); 77 | //每个角色拥有默认的权限 78 | String rolePermission = userMapper.getRolePermission(username); 79 | //每个用户可以设置新的权限 80 | String permission = userMapper.getPermission(username); 81 | Set roleSet = new HashSet<>(); 82 | Set permissionSet = new HashSet<>(); 83 | //需要将 role, permission 封装到 Set 作为 info.setRoles(), info.setStringPermissions() 的参数 84 | roleSet.add(role); 85 | permissionSet.add(rolePermission); 86 | permissionSet.add(permission); 87 | //设置该用户拥有的角色和权限 88 | info.setRoles(roleSet); 89 | info.setStringPermissions(permissionSet); 90 | return info; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/config/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.config; 2 | 3 | import com.howie.shirojwt.filter.JWTFilter; 4 | import com.howie.shirojwt.shiro.CustomRealm; 5 | import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; 6 | import org.apache.shiro.mgt.DefaultSubjectDAO; 7 | import org.apache.shiro.mgt.SecurityManager; 8 | import org.apache.shiro.spring.LifecycleBeanPostProcessor; 9 | import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 10 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 11 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 12 | import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | 16 | import javax.servlet.Filter; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Created with IntelliJ IDEA 22 | * 23 | * @Author yuanhaoyue swithaoy@gmail.com 24 | * @Description 25 | * @Date 2018-04-09 26 | * @Time 16:56 27 | */ 28 | @Configuration 29 | public class ShiroConfig { 30 | /** 31 | * 先走 filter ,然后 filter 如果检测到请求头存在 token,则用 token 去 login,走 Realm 去验证 32 | */ 33 | @Bean 34 | public ShiroFilterFactoryBean factory(SecurityManager securityManager) { 35 | ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); 36 | 37 | // 添加自己的过滤器并且取名为jwt 38 | Map filterMap = new LinkedHashMap<>(); 39 | //设置我们自定义的JWT过滤器 40 | filterMap.put("jwt", new JWTFilter()); 41 | factoryBean.setFilters(filterMap); 42 | factoryBean.setSecurityManager(securityManager); 43 | // 设置无权限时跳转的 url; 44 | factoryBean.setUnauthorizedUrl("/unauthorized/无权限"); 45 | Map filterRuleMap = new HashMap<>(); 46 | // 所有请求通过我们自己的JWT Filter 47 | filterRuleMap.put("/**", "jwt"); 48 | // 访问 /unauthorized/** 不通过JWTFilter 49 | filterRuleMap.put("/unauthorized/**", "anon"); 50 | factoryBean.setFilterChainDefinitionMap(filterRuleMap); 51 | return factoryBean; 52 | } 53 | 54 | /** 55 | * 注入 securityManager 56 | */ 57 | @Bean 58 | public SecurityManager securityManager(CustomRealm customRealm) { 59 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 60 | // 设置自定义 realm. 61 | securityManager.setRealm(customRealm); 62 | 63 | /* 64 | * 关闭shiro自带的session,详情见文档 65 | * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29 66 | */ 67 | DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); 68 | DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); 69 | defaultSessionStorageEvaluator.setSessionStorageEnabled(false); 70 | subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); 71 | securityManager.setSubjectDAO(subjectDAO); 72 | return securityManager; 73 | } 74 | 75 | /** 76 | * 添加注解支持 77 | */ 78 | @Bean 79 | public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { 80 | DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); 81 | // 强制使用cglib,防止重复代理和可能引起代理出错的问题 82 | // https://zhuanlan.zhihu.com/p/29161098 83 | defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); 84 | return defaultAdvisorAutoProxyCreator; 85 | } 86 | 87 | @Bean 88 | public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { 89 | AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); 90 | advisor.setSecurityManager(securityManager); 91 | return advisor; 92 | } 93 | 94 | @Bean 95 | public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { 96 | return new LifecycleBeanPostProcessor(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /shiroJWT/src/main/java/com/howie/shirojwt/filter/JWTFilter.java: -------------------------------------------------------------------------------- 1 | package com.howie.shirojwt.filter; 2 | 3 | import com.howie.shirojwt.shiro.JWTToken; 4 | import org.apache.shiro.authz.UnauthorizedException; 5 | import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | 12 | import javax.servlet.ServletRequest; 13 | import javax.servlet.ServletResponse; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.net.URLEncoder; 18 | 19 | /** 20 | * Created with IntelliJ IDEA 21 | * 22 | * @Author yuanhaoyue swithaoy@gmail.com 23 | * @Description preHandle->isAccessAllowed->isLoginAttempt->executeLogin 24 | * @Date 2018-04-08 25 | * @Time 12:36 26 | */ 27 | public class JWTFilter extends BasicHttpAuthenticationFilter { 28 | private Logger logger = LoggerFactory.getLogger(this.getClass()); 29 | 30 | /** 31 | * 如果带有 token,则对 token 进行检查,否则直接通过 32 | */ 33 | @Override 34 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException { 35 | //判断请求的请求头是否带上 "Token" 36 | if (isLoginAttempt(request, response)) { 37 | //如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确 38 | try { 39 | executeLogin(request, response); 40 | return true; 41 | } catch (Exception e) { 42 | //token 错误 43 | responseError(response, e.getMessage()); 44 | } 45 | } 46 | //如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true 47 | return true; 48 | } 49 | 50 | /** 51 | * 判断用户是否想要登入。 52 | * 检测 header 里面是否包含 Token 字段 53 | */ 54 | @Override 55 | protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) { 56 | HttpServletRequest req = (HttpServletRequest) request; 57 | String token = req.getHeader("Token"); 58 | return token != null; 59 | } 60 | 61 | /** 62 | * 执行登陆操作 63 | */ 64 | @Override 65 | protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { 66 | HttpServletRequest httpServletRequest = (HttpServletRequest) request; 67 | String token = httpServletRequest.getHeader("Token"); 68 | JWTToken jwtToken = new JWTToken(token); 69 | // 提交给realm进行登入,如果错误他会抛出异常并被捕获 70 | getSubject(request, response).login(jwtToken); 71 | // 如果没有抛出异常则代表登入成功,返回true 72 | return true; 73 | } 74 | 75 | /** 76 | * 对跨域提供支持 77 | */ 78 | @Override 79 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 80 | HttpServletRequest httpServletRequest = (HttpServletRequest) request; 81 | HttpServletResponse httpServletResponse = (HttpServletResponse) response; 82 | httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); 83 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); 84 | httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); 85 | // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态 86 | if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { 87 | httpServletResponse.setStatus(HttpStatus.OK.value()); 88 | return false; 89 | } 90 | return super.preHandle(request, response); 91 | } 92 | 93 | /** 94 | * 将非法请求跳转到 /unauthorized/** 95 | */ 96 | private void responseError(ServletResponse response, String message) { 97 | try { 98 | HttpServletResponse httpServletResponse = (HttpServletResponse) response; 99 | //设置编码,否则中文字符在重定向时会变为空字符串 100 | message = URLEncoder.encode(message, "UTF-8"); 101 | httpServletResponse.sendRedirect("/unauthorized/" + message); 102 | } catch (IOException e) { 103 | logger.error(e.getMessage()); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /shiroJWT/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /shiroSimple/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /shiroJWT/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /shiroSimple/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /shiro + springBoot 整合 JWT.md: -------------------------------------------------------------------------------- 1 | 2 | ## JWTUtil 3 | 我们利用 JWT 的工具类来生成我们的 token,这个工具类主要有生成 token 和 校验 token 两个方法 4 | 5 | 生成 token 时,指定 token 过期时间 ```EXPIRE_TIME``` 和签名密钥 ```SECRET```,然后将 date 和 username 写入 token 中,并使用带有密钥的 HS256 签名算法进行签名 6 | ``` 7 | Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); 8 | Algorithm algorithm = Algorithm.HMAC256(SECRET); 9 | JWT.create() 10 | .withClaim("username", username) 11 | //到期时间 12 | .withExpiresAt(date) 13 | //创建一个新的JWT,并使用给定的算法进行标记 14 | .sign(algorithm); 15 | ``` 16 | 17 | 18 | ## 数据库表 19 | ![user](https://upload-images.jianshu.io/upload_images/8807674-c67b741ce3f1f696.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 20 | role: 角色;permission: 权限;ban: 封号状态 21 | ![role](https://upload-images.jianshu.io/upload_images/8807674-218309c1ee80b8fa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 22 | 23 | 每个用户有对应的角色(user,admin),权限(normal,vip),而 user 角色默认权限为 normal, admin 角色默认权限为 vip(当然,user 也可以是 vip) 24 | 25 | ## 过滤器 26 | 在上一篇文章中,我们使用的是 shiro 默认的权限拦截 Filter,而因为 JWT 的整合,我们需要自定义自己的过滤器 JWTFilter,JWTFilter 继承了 BasicHttpAuthenticationFilter,并部分原方法进行了重写 27 | 28 | 该过滤器主要有三步: 29 | 1. 检验请求头是否带有 token ```((HttpServletRequest) request).getHeader("Token") != null``` 30 | 2. 如果带有 token,执行 shiro 的 login() 方法,将 token 提交到 Realm 中进行检验;如果没有 token,说明当前状态为游客状态(或者其他一些不需要进行认证的接口) 31 | ``` 32 | @Override 33 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException { 34 | //判断请求的请求头是否带上 "Token" 35 | if (((HttpServletRequest) request).getHeader("Token") != null) { 36 | //如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确 37 | try { 38 | executeLogin(request, response); 39 | return true; 40 | } catch (Exception e) { 41 | //token 错误 42 | responseError(response, e.getMessage()); 43 | } 44 | } 45 | //如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true 46 | return true; 47 | } 48 | 49 | @Override 50 | protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { 51 | HttpServletRequest httpServletRequest = (HttpServletRequest) request; 52 | String token = httpServletRequest.getHeader("Token"); 53 | JWTToken jwtToken = new JWTToken(token); 54 | // 提交给realm进行登入,如果错误他会抛出异常并被捕获 55 | getSubject(request, response).login(jwtToken); 56 | // 如果没有抛出异常则代表登入成功,返回true 57 | return true; 58 | } 59 | ``` 60 | 3. 如果在 token 校验的过程中出现错误,如 token 校验失败,那么我会将该请求视为认证不通过,则重定向到 ```/unauthorized/**``` 61 | 62 | 63 | 另外,我将跨域支持放到了该过滤器来处理 64 | 65 | ## Realm 类 66 | 依然是我们的自定义 Realm ,对这一块还不了解的可以先看我的上一篇 shiro 的文章 67 | - 身份认证 68 | ``` 69 | if (username == null || !JWTUtil.verify(token, username)) { 70 | throw new AuthenticationException("token认证失败!"); 71 | } 72 | String password = userMapper.getPassword(username); 73 | if (password == null) { 74 | throw new AuthenticationException("该用户不存在!"); 75 | } 76 | int ban = userMapper.checkUserBanStatus(username); 77 | if (ban == 1) { 78 | throw new AuthenticationException("该用户已被封号!"); 79 | } 80 | ``` 81 | 拿到传来的 token ,检查 token 是否有效,用户是否存在,以及用户的封号情况 82 | 83 | - 权限认证 84 | ``` 85 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 86 | //获得该用户角色 87 | String role = userMapper.getRole(username); 88 | //每个角色拥有默认的权限 89 | String rolePermission = userMapper.getRolePermission(username); 90 | //每个用户可以设置新的权限 91 | String permission = userMapper.getPermission(username); 92 | Set roleSet = new HashSet<>(); 93 | Set permissionSet = new HashSet<>(); 94 | //需要将 role, permission 封装到 Set 作为 info.setRoles(), info.setStringPermissions() 的参数 95 | roleSet.add(role); 96 | permissionSet.add(rolePermission); 97 | permissionSet.add(permission); 98 | //设置该用户拥有的角色和权限 99 | info.setRoles(roleSet); 100 | info.setStringPermissions(permissionSet); 101 | ``` 102 | 利用 token 中获得的 username,分别从数据库查到该用户所拥有的角色,权限,存入 SimpleAuthorizationInfo 中 103 | 104 | ## ShiroConfig 配置类 105 | 设置好我们自定义的 filter,并使所有请求通过我们的过滤器,除了我们用于处理未认证请求的 ```/unauthorized/**``` 106 | ``` 107 | @Bean 108 | public ShiroFilterFactoryBean factory(SecurityManager securityManager) { 109 | ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); 110 | 111 | // 添加自己的过滤器并且取名为jwt 112 | Map filterMap = new HashMap<>(); 113 | //设置我们自定义的JWT过滤器 114 | filterMap.put("jwt", new JWTFilter()); 115 | factoryBean.setFilters(filterMap); 116 | factoryBean.setSecurityManager(securityManager); 117 | Map filterRuleMap = new HashMap<>(); 118 | // 所有请求通过我们自己的JWT Filter 119 | filterRuleMap.put("/**", "jwt"); 120 | // 访问 /unauthorized/** 不通过JWTFilter 121 | filterRuleMap.put("/unauthorized/**", "anon"); 122 | factoryBean.setFilterChainDefinitionMap(filterRuleMap); 123 | return factoryBean; 124 | } 125 | ``` 126 | ## 权限控制注解 @RequiresRoles, @RequiresPermissions 127 | 这两个注解为我们主要的权限控制注解, 如 128 | ``` 129 | // 拥有 admin 角色可以访问 130 | @RequiresRoles("admin") 131 | ``` 132 | ``` 133 | // 拥有 user 或 admin 角色可以访问 134 | @RequiresRoles(logical = Logical.OR, value = {"user", "admin"}) 135 | ``` 136 | ``` 137 | // 拥有 vip 和 normal 权限可以访问 138 | @RequiresPermissions(logical = Logical.AND, value = {"vip", "normal"}) 139 | ``` 140 | ``` 141 | // 拥有 user 或 admin 角色,且拥有 vip 权限可以访问 142 | @GetMapping("/getVipMessage") 143 | @RequiresRoles(logical = Logical.OR, value = {"user", "admin"}) 144 | @RequiresPermissions("vip") 145 | public ResultMap getVipMessage() { 146 | return resultMap.success().code(200).message("成功获得 vip 信息!"); 147 | } 148 | ``` 149 | 当我们写的接口拥有以上的注解时,如果请求没有带有 token 或者带了 token 但权限认证不通过,则会报 UnauthenticatedException 异常,但是我在 ExceptionController 类对这些异常进行了集中处理 150 | ``` 151 | @ExceptionHandler(ShiroException.class) 152 | public ResultMap handle401() { 153 | return resultMap.fail().code(401).message("您没有权限访问!"); 154 | } 155 | ``` 156 | 这时,出现 shiro 相关的异常时则会返回 157 | ``` 158 | { 159 | "result": "fail", 160 | "code": 401, 161 | "message": "您没有权限访问!" 162 | } 163 | ``` 164 | 除了以上两种,还有 @RequiresAuthentication ,@RequiresUser 等注解 165 | 166 | ## 功能实现 167 | 用户角色分为三类,管理员 admin,普通用户 user,游客 guest;admin 默认权限为 vip,user 默认权限为 normal,当 user 升级为 vip 权限时可以访问 vip 权限的页面。 168 | 169 | 具体实现可以看源代码(开头已经给出地址) 170 | 171 | ### 登陆 172 | 登陆接口不带有 token,当登陆密码,用户名验证正确后返回 token。 173 | ``` 174 | @PostMapping("/login") 175 | public ResultMap login(@RequestParam("username") String username, 176 | @RequestParam("password") String password) { 177 | String realPassword = userMapper.getPassword(username); 178 | if (realPassword == null) { 179 | return resultMap.fail().code(401).message("用户名错误"); 180 | } else if (!realPassword.equals(password)) { 181 | return resultMap.fail().code(401).message("密码错误"); 182 | } else { 183 | return resultMap.success().code(200).message(JWTUtil.createToken(username)); 184 | } 185 | } 186 | ``` 187 | ``` 188 | { 189 | "result": "success", 190 | "code": 200, 191 | "message": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjUxODQyMzUsInVzZXJuYW1lIjoiaG93aWUifQ.fG5Qs739Hxy_JjTdSIx_iiwaBD43aKFQMchx9fjaCRo" 192 | } 193 | ``` 194 | 195 | ## 异常处理 196 | ``` 197 | // 捕捉shiro的异常 198 | @ExceptionHandler(ShiroException.class) 199 | public ResultMap handle401() { 200 | return resultMap.fail().code(401).message("您没有权限访问!"); 201 | } 202 | 203 | // 捕捉其他所有异常 204 | @ExceptionHandler(Exception.class) 205 | public ResultMap globalException(HttpServletRequest request, Throwable ex) { 206 | return resultMap.fail() 207 | .code(getStatus(request).value()) 208 | .message("访问出错,无法访问: " + ex.getMessage()); 209 | } 210 | ``` 211 | 212 | ## 权限控制 213 | - UserController(user 或 admin 可以访问) 214 | 在接口上带上 ```@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})``` 215 | - vip 权限 216 | 再加上```@RequiresPermissions("vip")``` 217 | 218 | - AdminController(admin 可以访问) 219 | 在接口上带上 ```@RequiresRoles("admin")``` 220 | 221 | - GuestController(所有人可以访问) 222 | 不做权限处理 223 | 224 | ## 测试结果 225 | ![不带 token](https://upload-images.jianshu.io/upload_images/8807674-8df75027832d8742.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 226 | ![带上 token](https://upload-images.jianshu.io/upload_images/8807674-8c2736e7daa1e303.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 227 | ![带上错误的 token](https://upload-images.jianshu.io/upload_images/8807674-79716d898d6e3359.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 228 | ![游客,无 token](https://upload-images.jianshu.io/upload_images/8807674-499c44828b453767.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 229 | ![访问无权限的接口(vip)](https://upload-images.jianshu.io/upload_images/8807674-b4087f6b38c32f11.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 230 | ![该用户已被封号](https://upload-images.jianshu.io/upload_images/8807674-555a7133a52d95a4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 231 | 232 | -------------------------------------------------------------------------------- /shiro 整合 springBoot 实现基本的角色权限控制.md: -------------------------------------------------------------------------------- 1 | ## 依赖包 2 | 3 | ``` 4 | 5 | org.apache.shiro 6 | shiro-spring 7 | 1.3.2 8 | 9 | ``` 10 | ## 数据库表 11 | 一切从简,用户 user 表,以及角色 role 表 12 | ![user](https://upload-images.jianshu.io/upload_images/8807674-14c71a60da237fbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 13 | 14 | ![role](https://upload-images.jianshu.io/upload_images/8807674-9bfb0a984fc9c28c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | 17 | ## Shiro 相关类 18 | ### Shiro 配置类 19 | ``` 20 | @Configuration 21 | public class ShiroConfig { 22 | @Bean 23 | public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { 24 | ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 25 | // 必须设置 SecurityManager 26 | shiroFilterFactoryBean.setSecurityManager(securityManager); 27 | // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射 28 | shiroFilterFactoryBean.setLoginUrl("/notLogin"); 29 | // 设置无权限时跳转的 url; 30 | shiroFilterFactoryBean.setUnauthorizedUrl("/notRole"); 31 | 32 | // 设置拦截器 33 | Map filterChainDefinitionMap = new LinkedHashMap<>(); 34 | //游客,开发权限 35 | filterChainDefinitionMap.put("/guest/**", "anon"); 36 | //用户,需要角色权限 “user” 37 | filterChainDefinitionMap.put("/user/**", "roles[user]"); 38 | //管理员,需要角色权限 “admin” 39 | filterChainDefinitionMap.put("/admin/**", "roles[admin]"); 40 | //开放登陆接口 41 | filterChainDefinitionMap.put("/login", "anon"); 42 | //其余接口一律拦截 43 | //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 44 | filterChainDefinitionMap.put("/**", "authc"); 45 | 46 | shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 47 | System.out.println("Shiro拦截器工厂类注入成功"); 48 | return shiroFilterFactoryBean; 49 | } 50 | 51 | /** 52 | * 注入 securityManager 53 | */ 54 | @Bean 55 | public SecurityManager securityManager() { 56 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 57 | // 设置realm. 58 | securityManager.setRealm(customRealm()); 59 | return securityManager; 60 | } 61 | 62 | /** 63 | * 自定义身份认证 realm; 64 | *

65 | * 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm, 66 | * 否则会影响 CustomRealm类 中其他类的依赖注入 67 | */ 68 | @Bean 69 | public CustomRealm customRealm() { 70 | return new CustomRealm(); 71 | } 72 | } 73 | ``` 74 | **注意**:里面的 SecurityManager 类导入的应该是 ```import org.apache.shiro.mgt.SecurityManager;``` 但是,如果你是复制代码过来的话,会默认导入 ```java.lang.SecurityManager``` 这里也稍稍有点坑,其他的类的话,也是都属于 shiro 包里面的类 75 | 76 | shirFilter 方法中主要是设置了一些重要的跳转 url,比如未登陆时,无权限时的跳转;以及设置了各类 url 的权限拦截,比如 /user 开始的 url 需要 user 权限,/admin 开始的 url 需要 admin 权限等 77 | 78 | #### 权限拦截 Filter 79 | 80 | 当运行一个Web应用程序时,Shiro将会创建一些有用的默认 Filter 实例,并自动地将它们置为可用,而这些默认的 Filter 实例是被 DefaultFilter 枚举类定义的,当然我们也可以自定义 Filter 实例,这些在以后的文章中会讲到 81 | ![DefaultFilter](https://upload-images.jianshu.io/upload_images/8807674-6ef51f395cf8c6b1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 82 | 83 | Filter | 解释 84 | ---|--- 85 | anon | 无参,开放权限,可以理解为匿名用户或游客 86 | authc | 无参,需要认证 87 | logout | 无参,注销,执行后会直接跳转到``` shiroFilterFactoryBean.setLoginUrl(); ``` 设置的 url 88 | authcBasic | 无参,表示 httpBasic 认证 89 | user | 无参,表示必须存在用户,当登入操作时不做检查 90 | ssl | 无参,表示安全的URL请求,协议为 https 91 | perms[user] | 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms["user, admin"],当有多个参数时必须每个参数都通过才算通过 92 | roles[admin] | 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles["admin,user"],当有多个参数时必须每个参数都通过才算通过 93 | rest[user] |根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 94 | port[8081] | 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数 95 | 96 | 常用的主要就是 anon,authc,user,roles,perms 等 97 | 98 | **注意**:anon, authc, authcBasic, user 是第一组认证过滤器,perms, port, rest, roles, ssl 是第二组授权过滤器,要通过授权过滤器,就先要完成登陆认证操作(即先要完成认证才能前去寻找授权) 才能走第二组授权器(例如访问需要 roles 权限的 url,如果还没有登陆的话,会直接跳转到 ``` shiroFilterFactoryBean.setLoginUrl(); ``` 设置的 url ) 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | ### 自定义 realm 类 108 | 我们首先要继承 AuthorizingRealm 类来自定义我们自己的 realm 以进行我们自定义的身份,权限认证操作。 109 | 记得要 Override 重写 doGetAuthenticationInfo 和 doGetAuthorizationInfo 两个方法(两个方法名很相似,不要搞错) 110 | ``` 111 | public class CustomRealm extends AuthorizingRealm { 112 | private UserMapper userMapper; 113 | 114 | @Autowired 115 | private void setUserMapper(UserMapper userMapper) { 116 | this.userMapper = userMapper; 117 | } 118 | 119 | /** 120 | * 获取身份验证信息 121 | * Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。 122 | * 123 | * @param authenticationToken 用户身份信息 token 124 | * @return 返回封装了用户信息的 AuthenticationInfo 实例 125 | */ 126 | @Override 127 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 128 | System.out.println("————身份认证方法————"); 129 | UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; 130 | // 从数据库获取对应用户名密码的用户 131 | String password = userMapper.getPassword(token.getUsername()); 132 | if (null == password) { 133 | throw new AccountException("用户名不正确"); 134 | } else if (!password.equals(new String((char[]) token.getCredentials()))) { 135 | throw new AccountException("密码不正确"); 136 | } 137 | return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName()); 138 | } 139 | 140 | /** 141 | * 获取授权信息 142 | * 143 | * @param principalCollection 144 | * @return 145 | */ 146 | @Override 147 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 148 | System.out.println("————权限认证————"); 149 | String username = (String) SecurityUtils.getSubject().getPrincipal(); 150 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 151 | //获得该用户角色 152 | String role = userMapper.getRole(username); 153 | Set set = new HashSet<>(); 154 | //需要将 role 封装到 Set 作为 info.setRoles() 的参数 155 | set.add(role); 156 | //设置该用户拥有的角色 157 | info.setRoles(set); 158 | return info; 159 | } 160 | } 161 | ``` 162 | 重写的两个方法分别是实现身份认证以及权限认证,shiro 中有个作登陆操作的 ``` Subject.login() ``` 方法,当我们把封装了用户名,密码的 token 作为参数传入,便会跑进这两个方法里面(不一定两个方法都会进入) 163 | 164 | 其中 doGetAuthorizationInfo 方法只有在需要权限认证时才会进去,比如前面配置类中配置了 ``` filterChainDefinitionMap.put("/admin/**", "roles[admin]");``` 的管理员角色,这时进入 /admin 时就会进入 doGetAuthorizationInfo 方法来检查权限;而 doGetAuthenticationInfo 方法则是需要身份认证时(比如前面的 ```Subject.login()``` 方法)才会进入 165 | 166 | 再说下 UsernamePasswordToken 类,我们可以从该对象拿到登陆时的用户名和密码(登陆时会使用 ```new UsernamePasswordToken(username, password);```),而 get 用户名或密码有以下几个方法 167 | ``` 168 | token.getUsername() //获得用户名 String 169 | token.getPrincipal() //获得用户名 Object 170 | token.getPassword() //获得密码 char[] 171 | token.getCredentials() //获得密码 Object 172 | ``` 173 | 174 | **注意**:有很多人会发现,UserMapper 等类,接口无法通过 @Autowired 注入进来,跑程序的时候会报 NullPointerException,网上说了很多诸如是 Spring 加载顺序等原因,但其实有一个很重要的地方要大家注意,CustomRealm 这个类是在 shiro 配置类的 ```securityManager.setRealm() ``` 方法中设置进去的,而很多人直接写```securityManager.setRealm(new CustomRealm());``` ,这样是不行的,必须要使用 @Bean 注入 MyRealm,不能直接 new 对象: 175 | ``` 176 | @Bean 177 | public SecurityManager securityManager() { 178 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 179 | // 设置realm. 180 | securityManager.setRealm(customRealm()); 181 | return securityManager; 182 | } 183 | 184 | @Bean 185 | public CustomRealm customRealm() { 186 | return new CustomRealm(); 187 | } 188 | ``` 189 | 道理也很简单,和 Controller 中调用 Service 一样,都是 SpringBean,不能自己 new 190 | 191 | 当然,同样的道理也可以这样写: 192 | ``` 193 | @Bean 194 | public SecurityManager securityManager(CustomRealm customRealm) { 195 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 196 | // 设置realm. 197 | securityManager.setRealm(customRealm); 198 | return securityManager; 199 | } 200 | ``` 201 | 然后只要在 CustomRealm 类加上个类似 @Component 的注解即可 202 | 203 | ## 功能实现 204 | 本文的功能全部以接口返回 json 数据的方式实现 205 | ### 根据 url 权限分配 controller 206 | ``` 207 | 游客 208 | @RestController 209 | @RequestMapping("/guest") 210 | public class GuestController{ 211 | @Autowired 212 | private final ResultMap resultMap; 213 | 214 | @RequestMapping(value = "/enter", method = RequestMethod.GET) 215 | public ResultMap login() { 216 | return resultMap.success().message("欢迎进入,您的身份是游客"); 217 | } 218 | 219 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 220 | public ResultMap submitLogin() { 221 | return resultMap.success().message("您拥有获得该接口的信息的权限!"); 222 | } 223 | } 224 | ``` 225 | ``` 226 | 普通登陆用户 227 | @RestController 228 | @RequestMapping("/user") 229 | public class UserController{ 230 | @Autowired 231 | private final ResultMap resultMap; 232 | 233 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 234 | public ResultMap getMessage() { 235 | return resultMap.success().message("您拥有用户权限,可以获得该接口的信息!"); 236 | } 237 | } 238 | ``` 239 | ``` 240 | 管理员 241 | @RestController 242 | @RequestMapping("/admin") 243 | public class AdminController { 244 | @Autowired 245 | private final ResultMap resultMap; 246 | 247 | @RequestMapping(value = "/getMessage", method = RequestMethod.GET) 248 | public ResultMap getMessage() { 249 | return resultMap.success().message("您拥有管理员权限,可以获得该接口的信息!"); 250 | } 251 | } 252 | ``` 253 | 突然注意到 CustomRealm 类那里抛出了 AccountException 异常,现在建个类进行异常捕获 254 | ``` 255 | @RestControllerAdvice 256 | public class ExceptionController { 257 | private final ResultMap resultMap; 258 | 259 | @Autowired 260 | public ExceptionController(ResultMap resultMap) { 261 | this.resultMap = resultMap; 262 | } 263 | 264 | // 捕捉 CustomRealm 抛出的异常 265 | @ExceptionHandler(AccountException.class) 266 | public ResultMap handleShiroException(Exception ex) { 267 | return resultMap.fail().message(ex.getMessage()); 268 | } 269 | } 270 | ``` 271 | 还有进行登陆等处理的 LoginController 272 | ``` 273 | @RestController 274 | public class LoginController { 275 | @Autowired 276 | private ResultMap resultMap; 277 | private UserMapper userMapper; 278 | 279 | @RequestMapping(value = "/notLogin", method = RequestMethod.GET) 280 | public ResultMap notLogin() { 281 | return resultMap.success().message("您尚未登陆!"); 282 | } 283 | 284 | @RequestMapping(value = "/notRole", method = RequestMethod.GET) 285 | public ResultMap notRole() { 286 | return resultMap.success().message("您没有权限!"); 287 | } 288 | 289 | @RequestMapping(value = "/logout", method = RequestMethod.GET) 290 | public ResultMap logout() { 291 | Subject subject = SecurityUtils.getSubject(); 292 | //注销 293 | subject.logout(); 294 | return resultMap.success().message("成功注销!"); 295 | } 296 | 297 | /** 298 | * 登陆 299 | * 300 | * @param username 用户名 301 | * @param password 密码 302 | */ 303 | @RequestMapping(value = "/login", method = RequestMethod.POST) 304 | public ResultMap login(String username, String password) { 305 | // 从SecurityUtils里边创建一个 subject 306 | Subject subject = SecurityUtils.getSubject(); 307 | // 在认证提交前准备 token(令牌) 308 | UsernamePasswordToken token = new UsernamePasswordToken(username, password); 309 | // 执行认证登陆 310 | subject.login(token); 311 | //根据权限,指定返回数据 312 | String role = userMapper.getRole(username); 313 | if ("user".equals(role)) { 314 | return resultMap.success().message("欢迎登陆"); 315 | } 316 | if ("admin".equals(role)) { 317 | return resultMap.success().message("欢迎来到管理员页面"); 318 | } 319 | return resultMap.fail().message("权限错误!"); 320 | } 321 | } 322 | ``` 323 | ### 测试 324 | ![登陆前访问信息接口](https://upload-images.jianshu.io/upload_images/8807674-7048df42b04f5763.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 325 | 326 | 327 | ![普通用户登陆](https://upload-images.jianshu.io/upload_images/8807674-52bda59ed16c2de4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 328 | 329 | ![密码错误](https://upload-images.jianshu.io/upload_images/8807674-cb0266faa390c80f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 330 | 331 | ![管理员登陆](https://upload-images.jianshu.io/upload_images/8807674-2b07ade4277c9f3d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 332 | 333 | ![获取信息](https://upload-images.jianshu.io/upload_images/8807674-86ce356a15a49615.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 334 | 335 | ![获取信息](https://upload-images.jianshu.io/upload_images/8807674-cb377665ca151889.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 336 | 337 | ![注销](https://upload-images.jianshu.io/upload_images/8807674-3fc0b74103c51b2c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 338 | --------------------------------------------------------------------------------