文章目录

  • Spring Boot快速整合Shiro
    • 1、创建Demo
    • 2、Shiro实现登陆拦截
    • 3、Shiro实现用户认证
    • 4、Shiro整合Mybatis-Plus
    • 5、Shiro整合MD5盐值加密
    • 6、Shiro实现授权
    • 7、Shiro整合JWT+MD5

Spring Boot快速整合Shiro

1、创建Demo

  • 创建Spring Boot项目

我的用的是2.7.9版本的

  • 导入坐标
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.11.0</version></dependency>
</dependencies>
  • 简单配置

自定义Realm

// 自定义
public class UserRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权=》doGetAuthorizationInfo");return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证=》doGetAuthenticationInfo");return null;}
}

ShiroConfig配置类

@Configuration
public class ShiroConfig {// 创建realm类,需要自定义类@Beanpublic UserRealm userRealm(){return new UserRealm();}// DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 关联UserRealmsecurityManager.setRealm(userRealm);return securityManager;}// ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);return bean;}
}
  • 设置几个页面

add.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>

del.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>del</h1>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a th:href="@{/user/add}">add</a> <br><a th:href="@{/user/del}">del</a></body>
</html>
  • 配置Controller
@Controller
public class MyController {@RequestMapping("/")public String toIndex(Model model){model.addAttribute("msg", "Hello World");return "index";}@RequestMapping("/user/add")public String add(){return "user/add";}@RequestMapping("/user/del")public String del(){return "user/del";}
}

这样就在启动项目后,就可以看到,现在是可以自由切换,下面进行更深的配置

2、Shiro实现登陆拦截

  • 新增登录页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<form action=""><p>用户名:<input type="text" name="username"> </p><br><p>密码:<input type="password" name="password"> </p><br><p><input type="submit"> </p>
</form>
</body>
</html>
@RequestMapping("/toLogin")
public String toLogin(){return "login";
}
  • 配置ShiroConfig
@Configuration
public class ShiroConfig {// 创建realm类,需要自定义类@Beanpublic UserRealm userRealm(){return new UserRealm();}// DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 关联UserRealmsecurityManager.setRealm(userRealm);return securityManager;}// ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);// 添加Shiro的内置过滤器/***  anno:无需认证就可以访问*  authc:必须认证了才能访问*  user:必须拥有记住我功能才能用*  perms:拥有对某个资源的权限才能访问*  role:拥有某个角色权限才能访问*/// 使用map来指定过滤器Map<String, String> map = new LinkedHashMap<String, String>();// map.put("/user/*", "authc")map.put("/user/add", "authc");map.put("/user/del", "authc");bean.setFilterChainDefinitionMap(map);// 设置登录页面bean.setLoginUrl("/toLogin");return bean;}
}

如上设置了过滤器还有,没有认证的要跳转到登录的页面

3、Shiro实现用户认证

  • 在Controller中加一个login
@RequestMapping("/login")
public String login(String username, String password, Model model){// 获取当前用户Subject subject = SecurityUtils.getSubject();// 封装用户的登录数据UsernamePasswordToken token = new UsernamePasswordToken(username, password);// 登录 执行登录的方法try {subject.login(token);return "index";} catch (UnknownAccountException e) { // 用户名不存在model.addAttribute("msg", "用户名错误");return "login";} catch (IncorrectCredentialsException e){// 密码错误model.addAttribute("msg", "密码错误");return "login";}
}
  • 自定义UserRealm中做认证处理
// 自定义
public class UserRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权=》doGetAuthorizationInfo");return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了认证=》doGetAuthenticationInfo");// 用户名、密码  对接数据库String username = "root";  // 先伪造一手String password = "123";UsernamePasswordToken userToken = (UsernamePasswordToken) token;// 如果账号不正确就认证失败if(!userToken.getUsername().equals(username)) return null;// 密码认证是Shiro帮你做了return new SimpleAuthenticationInfo("", password, "");}
}
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p th:text="${msg}" style="color: red;"></p>
<h1>登录页面</h1>
<form th:action="@{/login}"><p>用户名:<input type="text" name="username"> </p><br><p>密码:<input type="password" name="password"> </p><br><p><input type="submit"> </p>
</form>
</body>
</html>

4、Shiro整合Mybatis-Plus

  • 坐标
<!--数据库相关坐标--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version>
</dependency><!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.23</version>
</dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version>
</dependency>
  • 配置文件application.yaml
spring:datasource:username: rootpassword: lige0612url: jdbc:mysql://localhost:3306/codingmore?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:#初始化连接池大小initial-size: 5#配置最小连接数min-idle: 5#配置最大连接数max-active: 200#配置连接等待超时时间max-wait: 60000#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒time-between-eviction-runs-millis: 60000#配置一个连接在池中最小生存的时间,单位是毫秒min-evictable-idle-time-millis: 300000#测试连接validation-query: SELECT 1 FROM DUAL#申请连接的时候检测,建议配置为true,不影响性能,并且保证安全test-while-idle: true#获取连接时执行检测,建议关闭,影响性能test-on-borrow: false#归还连接时执行检测,建议关闭,影响性能test-on-return: false#是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭pool-prepared-statements: false#开启poolPreparedStatements后生效max-pool-prepared-statement-per-connection-size: 20#配置扩展插件,常用的插件有=>stat:监控统计  log4j:日志  wall:防御sql注入filters: stat,wall,slf4j#打开mergeSql功能;慢SQL记录connection-properties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000#配置DruidStatFilterweb-stat-filter:enabled: trueurl-pattern: "/*"exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"#配置DruidStatViewServletstat-view-servlet:url-pattern: "/druid/*"allow: 127.0.0.1#登录名login-username: root#登录密码login-password: rootenabled: true
server:port: 8080
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: ASSIGN_IDtype-aliases-package: com.lzy.shiro_springboot.pojo
  • 数据库
  • 老生常谈

pojo

public class User {private Integer id;private String name;private String pwd;public User(Integer id, String name, String password) {this.id = id;this.name = name;this.pwd = password;}public User() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return pwd;}public void setPassword(String password) {this.pwd = password;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", password='" + pwd + '\'' +'}';}
}

mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {}

service 和 serviceImpl

public interface UserService extends IService<User> {}@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{}
  • 自定义realm接入数据库
// 自定义
public class UserRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权=》doGetAuthorizationInfo");return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了认证=》doGetAuthenticationInfo");// 用户名、密码  对接数据库UsernamePasswordToken userToken = (UsernamePasswordToken) token;// 连接真实的数据库LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.eq(User::getName, userToken.getUsername());User user = userService.getOne(lqw);if(user == null) {return null;}// 密码认证是Shiro帮你做了,Shiro加密return new SimpleAuthenticationInfo("", user.getPassword(), "");}
}

此时就可以将数据库中的数据接入到Realm中,实现输入的账号密码对接数据库

5、Shiro整合MD5盐值加密

  • 数据库中加入盐值的字段

  • pojo对应的实体类

  • Md5Utils

public class Md5Utils {public static String getSalt(int n){char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray();StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < n; i++) {char c = chars[new Random().nextInt(chars.length)];stringBuilder.append(c);}return stringBuilder.toString();}public static List<String> encryption(String password){List<String> msg = new ArrayList<>();String salt = getSalt(10);msg.add(salt);Md5Hash MD5 = new Md5Hash(password, salt, 1024);msg.add(MD5.toHex());// 其中msg[0]是盐值,msg[1]是加密后的密码,可以一并保存至数据库中。return msg;}
}
  • 配置密码匹配器

ShiroConfig

/*** 密码匹配器* @return HashedCredentialsMatcher*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();// 设置哈希算法名称matcher.setHashAlgorithmName("MD5");// 设置哈希迭代次数matcher.setHashIterations(1024);// 设置存储凭证(true:十六进制编码,false:base64)matcher.setStoredCredentialsHexEncoded(true);return matcher;
}// 创建realm类,需要自定义类
@Bean
public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher){UserRealm userRealm = new UserRealm();userRealm.setCredentialsMatcher(matcher);return userRealm;
}

  • 接入Md5加密

MyRealm

// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了认证=》doGetAuthenticationInfo");// 用户名、密码  对接数据库UsernamePasswordToken userToken = (UsernamePasswordToken) token;// 连接真实的数据库LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.eq(User::getName, userToken.getUsername());User user = userService.getOne(lqw);if(user != null) {return new SimpleAuthenticationInfo(user, user.getPwd(), ByteSource.Util.bytes(user.getSalt()), getName());}return null;
}

此时你可以使用Md5工具去生成几个例子,将数据库中的密码和盐值换成你生成,然后去试一试,如果要注册的时候,在注册逻辑中把密码加密,和盐值的逻辑加入即可

6、Shiro实现授权

  • 数据库

加一个权限的字段

修改pojo对应的实体类

进行授权判断
ShiroConfig

// ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);// 添加Shiro的内置过滤器/***  anno:无需认证就可以访问*  authc:必须认证了才能访问*  user:必须拥有记住我功能才能用*  perms:拥有对某个资源的权限才能访问*  role:拥有某个角色权限才能访问*/// 使用map来指定过滤器Map<String, String> map = new LinkedHashMap<>();// 授权map.put("/user/add", "perms[user:add]");map.put("/user/del", "perms[user:del]");// 拦截map.put("/user/*", "authc");bean.setFilterChainDefinitionMap(map);// 设置登录页面bean.setLoginUrl("/toLogin");// 未授权跳转bean.setUnauthorizedUrl("/noauth");return bean;
}

MyRealm

// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权=》doGetAuthorizationInfo");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 拿到当前用户对象Subject subject = SecurityUtils.getSubject();// 拿到用户对象User currentUser = (User)subject.getPrincipal();// 对接数据库权限info.addStringPermission(currentUser.getPerms());return info;
}

现在就是先登录,然后根据你数据库中的权限去做授权,然后你就能访问你对应的页面了

7、Shiro整合JWT+MD5

找了好多的资料。最后发现一个差不多的,就这样吧

  • 坐标
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version>
</dependency>
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.2</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>
</dependency>
  • 数据源
    application.yaml
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version>
</dependency>
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.2</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>
</dependency>

三层

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserBean {private String username;private String password;private String role;private String permission;
}@Mapper
public interface UserMapper extends BaseMapper<UserBean> {}public interface UserService extends IService<UserBean> {}@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserBean> implements UserService {}

JWTUtil

public class JWTUtil {// 过期时间5分钟private static final long EXPIRE_TIME = 5*60*1000;/*** 校验token是否正确* @param token 密钥* @param secret 用户的密码* @return 是否正确*/public static boolean verify(String token, String username, String secret) {try {Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception exception) {return false;}}/*** 获得token中的信息无需secret解密也能获得* @return token中包含的用户名*/public static String getUsername(String token) {try {DecodedJWT jwt = JWT.decode(token);return jwt.getClaim("username").asString();} catch (JWTDecodeException e) {return null;}}/*** 生成签名,5min后过期* @param username 用户名* @param secret 用户的密码* @return 加密的token*/public static String sign(String username, String secret) {try {Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(secret);// 附带username信息return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);} catch (UnsupportedEncodingException e) {return null;}}
}

restful

public class ResponseBean {// http 状态码private int code;// 返回信息private String msg;// 返回的数据private Object data;public ResponseBean(int code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}
}

自定义异常类

public class UnauthorizedException extends RuntimeException {public UnauthorizedException(String msg) {super(msg);}public UnauthorizedException() {super();}
}

Controller

@RestController
public class WebController {private static final Logger LOGGER = LogManager.getLogger(WebController.class);private UserService userService;@Autowiredpublic void setService(UserService userService) {this.userService = userService;}@PostMapping("/login")public ResponseBean login(@RequestParam("username") String username,@RequestParam("password") String password) {LambdaQueryWrapper<UserBean> lqw = new LambdaQueryWrapper<>();lqw.eq(UserBean::getUsername, username);UserBean userBean = userService.getOne(lqw);if (userBean.getPassword().equals(DigestUtil.md5Hex(password))) {return new ResponseBean(200, "Login success", JWTUtil.sign(username, DigestUtil.md5Hex(password)));} else {throw new UnauthorizedException();}}@GetMapping("/article")public ResponseBean article() {Subject subject = SecurityUtils.getSubject();if (subject.isAuthenticated()) {return new ResponseBean(200, "You are already logged in", null);} else {return new ResponseBean(200, "You are guest", null);}}@GetMapping("/require_auth")@RequiresAuthenticationpublic ResponseBean requireAuth() {return new ResponseBean(200, "You are authenticated", null);}@GetMapping("/require_role")@RequiresRoles("admin")public ResponseBean requireRole() {return new ResponseBean(200, "You are visiting require_role", null);}@GetMapping("/require_permission")@RequiresPermissions(logical = Logical.AND, value = {"view", "edit"})public ResponseBean requirePermission() {return new ResponseBean(200, "You are visiting permission require edit,view", null);}@RequestMapping(path = "/401")@ResponseStatus(HttpStatus.UNAUTHORIZED)public ResponseBean unauthorized() {return new ResponseBean(401, "Unauthorized", null);}
}

这里在login的时候,判断输入进来的password的md5加密后的字符串是否与从数据库中查询的一样,一样的话,采取生成Token

  • 处理框架异常
@RestControllerAdvice
public class ExceptionController {// 捕捉shiro的异常@ResponseStatus(HttpStatus.UNAUTHORIZED)@ExceptionHandler(ShiroException.class)public ResponseBean handle401(ShiroException e) {return new ResponseBean(401, e.getMessage(), null);}// 捕捉UnauthorizedException@ResponseStatus(HttpStatus.UNAUTHORIZED)@ExceptionHandler(UnauthorizedException.class)public ResponseBean handle401() {return new ResponseBean(401, "Unauthorized", null);}// 捕捉其他所有异常@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseBean globalException(HttpServletRequest request, Throwable ex) {return new ResponseBean(getStatus(request).value(), ex.getMessage(), null);}private HttpStatus getStatus(HttpServletRequest request) {Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");if (statusCode == null) {return HttpStatus.INTERNAL_SERVER_ERROR;}return HttpStatus.valueOf(statusCode);}
}
  • JWTToken
public class JWTToken implements AuthenticationToken {// 密钥private String token;public JWTToken(String token) {this.token = token;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;}
}
  • 实现Realm
@Service
public class MyRealm extends AuthorizingRealm {private static final Logger LOGGER = LogManager.getLogger(MyRealm.class);private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}/*** 大坑!,必须重写此方法,不然Shiro会报错*/@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof JWTToken;}/*** 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = JWTUtil.getUsername(principals.toString());LambdaQueryWrapper<UserBean> lqw = new LambdaQueryWrapper<>();lqw.eq(UserBean::getUsername, username);UserBean user = userService.getOne(lqw);SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();simpleAuthorizationInfo.addRole(user.getRole());Set<String> permission = new HashSet<>(Arrays.asList(user.getPermission().split(",")));simpleAuthorizationInfo.addStringPermissions(permission);return simpleAuthorizationInfo;}/*** 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {String token = (String) auth.getCredentials();// 解密获得username,用于和数据库进行对比String username = JWTUtil.getUsername(token);if (username == null) {throw new AuthenticationException("token invalid");}LambdaQueryWrapper<UserBean> lqw = new LambdaQueryWrapper<>();lqw.eq(UserBean::getUsername, username);UserBean userBean = userService.getOne(lqw);if (userBean == null) {throw new AuthenticationException("User didn't existed!");}if (!JWTUtil.verify(token, username, userBean.getPassword())) {throw new AuthenticationException("Username or password error");}return new SimpleAuthenticationInfo(token, token, "my_realm");}
}

鉴权和认证最终都是在这里,这里我觉得认证和Spring Secuity是差不多的,只不过Spring Secuity可以一个api就搞完了,顺便还能验证加密的账户信息,而我这里在login是手动的去做的

  • JWTFilter
public class JWTFilter extends BasicHttpAuthenticationFilter {private Logger LOGGER = LoggerFactory.getLogger(this.getClass());/*** 判断用户是否想要登入。* 检测header里面是否包含Authorization字段即可*/@Overrideprotected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {HttpServletRequest req = (HttpServletRequest) request;String authorization = req.getHeader("Authorization");return authorization != null;}/****/@Overrideprotected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;String authorization = httpServletRequest.getHeader("Authorization");JWTToken token = new JWTToken(authorization);// 提交给realm进行登入,如果错误他会抛出异常并被捕获getSubject(request, response).login(token);// 如果没有抛出异常则代表登入成功,返回truereturn true;}/*** 这里我们详细说明下为什么最终返回的都是true,即允许访问* 例如我们提供一个地址 GET /article* 登入用户和游客看到的内容是不同的* 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西* 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入* 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可* 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {if (isLoginAttempt(request, response)) {try {executeLogin(request, response);} catch (Exception e) {response401(request, response);}}return true;}/*** 对跨域提供支持*/@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;HttpServletResponse httpServletResponse = (HttpServletResponse) response;httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpServletResponse.setStatus(HttpStatus.OK.value());return false;}return super.preHandle(request, response);}/*** 将非法请求跳转到 /401*/private void response401(ServletRequest req, ServletResponse resp) {try {HttpServletResponse httpServletResponse = (HttpServletResponse) resp;httpServletResponse.sendRedirect("/401");} catch (IOException e) {LOGGER.error(e.getMessage());}}
}

我觉得这个过滤器更像是一个路由,getSubject(request, response).login(token);这个还是重点

  • ShiroConfig
@Configuration
public class ShiroConfig {@Bean("securityManager")public DefaultWebSecurityManager getManager(MyRealm realm) {DefaultWebSecurityManager manager = new DefaultWebSecurityManager();// 使用自己的realmmanager.setRealm(realm);/** 关闭shiro自带的session,详情见文档* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29*/DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);manager.setSubjectDAO(subjectDAO);return manager;}@Bean("shiroFilter")public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();// 添加自己的过滤器并且取名为jwtMap<String, Filter> filterMap = new HashMap<>();filterMap.put("jwt", new JWTFilter());factoryBean.setFilters(filterMap);factoryBean.setSecurityManager(securityManager);factoryBean.setUnauthorizedUrl("/401");/** 自定义url规则* http://shiro.apache.org/web.html#urls-*/Map<String, String> filterRuleMap = new HashMap<>();// 所有请求通过我们自己的JWT FilterfilterRuleMap.put("/**", "jwt");// 访问401和404页面不通过我们的FilterfilterRuleMap.put("/401", "anon");factoryBean.setFilterChainDefinitionMap(filterRuleMap);return factoryBean;}/*** 下面的代码是添加注解支持*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();// 强制使用cglib,防止重复代理和可能引起代理出错的问题// https://zhuanlan.zhihu.com/p/29161098defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}
}

首先是关掉了Shiro的默认的session,然后去做拦截器规则和拦截的配置,将我们前面配置的JwtFilter拿过来,意思就是把所有的请求都拦截到,统一放到JwtFilter去做处理

参考https://segmentfault.com/a/1190000039843857

Spring Boot快速整合Shiro相关推荐

  1. 使用 Spring Boot 快速构建 Spring 框架应用

    https://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/index.html Spring 框架对于很多 Java 开发人员来说都不陌生 ...

  2. 使用 Spring Boot 快速构建 Spring 框架应用--转

    原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/ Spring 框架对于很多 Java 开发人员来说都不陌生.自从 2 ...

  3. 解密电商系统-Spring boot快速开始及核心功能介绍(下)

    上次说了Spring boot快速开始及核心功能介绍,本次说说配置文件相关的. Spring Boot属性配置文件详解(一) 修改端口 # application.properties: server ...

  4. 10个Spring Boot快速开发的项目,接私活利器(快速、高效)

    本文为大家精选了 码云 上优秀的 Spring Boot 语言开源项目,涵盖了企业级系统框架.文件文档系统.秒杀系统.微服务化系统.后台管理系统等,希望能够给大家带来一点帮助:) 1.项目名称:分布式 ...

  5. 视频教程-Spring boot快速入门-Java

    Spring boot快速入门 十年项目开发经验,主要从事java相关的开发,熟悉各种mvc开发框架. 王振伟 ¥12.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 ...

  6. spring boot 学习(二)spring boot 框架整合 thymeleaf

    spring boot 框架整合 thymeleaf spring boot 的官方文档中建议开发者使用模板引擎,避免使用 JSP.因为若一定要使用 JSP 将无法使用. 注意:本文主要参考学习了大神 ...

  7. 关于Spring Boot WebSocket整合以及nginx配置详解

    这篇文章主要给大家介绍了关于Spring Boot WebSocket整合以及nginx配置的相关资料,文中通过示例代码给大家介绍的非常详细,相信对大家的学习或者工作具有一定的参考学习价值,需要的朋友 ...

  8. Spring Boot 应用系列 5 -- Spring Boot 2 整合logback

    上一篇我们梳理了Spring Boot 2 整合log4j2的配置过程,其中讲到了Spring Boot 2原装适配logback,并且在非异步环境下logback和log4j2的性能差别不大,所以对 ...

  9. Spring boot Mybatis 整合(注解版)

    之前写过一篇关于springboot 与 mybatis整合的博文,使用了一段时间spring-data-jpa,发现那种方式真的是太爽了,mybatis的xml的映射配置总觉得有点麻烦.接口定义和映 ...

最新文章

  1. 控制C++的类只能在堆分配或只能在栈分配
  2. Exchange Server 2007迁移Exchange Server 2010 (16)--- OWA重定向
  3. IdentityServer4系列 | 简化模式
  4. linux java -xms_java.lang.OutOfMemoryError及解决方法
  5. (转)GDB 使用方法
  6. php映射,PHP实现路由映射到指定控制器
  7. 涉密计算机 桌面 及 屏保,符合国家保密要求的涉密计算机屏幕保护程序启动时间要求是不超10分钟 - 作业在线问答...
  8. 零基础新手如何自学PS
  9. 网络教育统考计算机和英语作文,网络教育英语统考试题
  10. Xilinx 7系列FPGA DDR3硬件设计规则
  11. 抖音5×5一笔连线问题无解的证明
  12. 在Windows下使用Tortoise Git时,提示认证失败的解决办法
  13. Cell子刊:北大姜长涛组发现HIF-2α通过肠道菌群调控脂肪产热
  14. pmf源解析_科研进展 | 不同燃烧排放的一次有机物源谱特征及其在源解析中的应用...
  15. 【开发经验】java代码中实现限流
  16. b ,B,KB,MB,GB之间的关系
  17. ospf在NBMA网络中的实验(保姆级别)
  18. 人工智能中的图灵测试
  19. xshell里面使用黑色背景时蓝色字体看不清楚的解决方法
  20. 上海热门共享办公室租赁平台

热门文章

  1. pythom-day20-logging模块
  2. css中首字大小,css如何设置字体大小
  3. 使用nvm下载node
  4. python中if有多个条件_Python中if有多个条件处理方法
  5. p51 thinkpad 拆解_Thinkpad P53S笔记本电脑拆解教程(T490S/P43S通用)及扩展坞评测
  6. [WPF] 考古Expression Web:微软当年最漂亮的WPF软件
  7. C++ signal信号(SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGTRAP、SIGABRT等等的说明)
  8. Vue中动态设置img的src不生效的原因
  9. 检查有日文片假名的新闻
  10. Adopt Open JDK官方文档(二)如何使用导航和取得进步?