# shiro-spring-boot-sample
**Repository Path**: wzk9261/shiro-spring-boot-sample
## Basic Information
- **Project Name**: shiro-spring-boot-sample
- **Description**: spring boot 集成 shiro
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-03-29
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Spring Boot + Shiro 集成
Shiro 是一个流行的 Java 安全框架。
其实 Spring 有一个 Spring Security 的安全框架,我试用了一下,太复杂了。同样的安全需求,用 Shiro 要简单、快捷得多,也利于理解。
本手册的源码托管在 GitHub 上:
[YorkeCao/shiro-spring-boot-sample](https://github.com/YorkeCao/shiro-spring-boot-sample)
下面主要介绍在 Spring Boot 项目中引入 Shiro,对应用进行安全管控。
方法
## 集成
可以利用 Shiro 启动器来完成与 Spring Boot 的集成。
这里为了简单,尽量少做配置。实际上 Shiro 有很多自定义选项。详细介绍请移步[官网](http://shiro.apache.org/)。
1. **引入 Shiro 启动器**
Shiro 官方提供了 Spring Boot 启动器:[shiro-spring-boot-starter](http://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-starter),在 `pom.xml` 文件中引入:
```xml
org.apache.shiro
shiro-spring-boot-starter
1.4.0
```
2. **配置 Shiro**
这里我们提供一个最简单的 Java Class 配置。
里面用到了一个自定义的 Realm:CustomRealm。
```java
package io.yorkecao.sample.config;
import io.yorkecao.sample.shiro.CustomRealm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Yorke
*/
@Configuration
public class ShiroConfig {
@Bean(name = "customRealm")
public CustomRealm customRealm() {
return new CustomRealm();
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(CustomRealm customRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager ();
securityManager.setRealm(customRealm);
return securityManager;
}
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
}
```
3. **实现自定义 Realm**
Realm 是控制认证和授权的核心部分,也是开发人员必须自己实现的部分。
实现自定义 Realm 最快捷的方式是继承 AuthorizingRealm 类。
```java
package io.yorkecao.sample.shiro;
import io.yorkecao.sample.dao.ShiroSampleDao;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
/**
* @author Yorke
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private ShiroSampleDao shiroSampleDao;
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = this.shiroSampleDao.getPasswordByUsername(username);
return new SimpleAuthenticationInfo(username, password, getName());
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) super.getAvailablePrincipal(principalCollection);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
Set roles = shiroSampleDao.getRolesByUsername(username);
authorizationInfo.setRoles(roles);
roles.forEach(role -> {
Set permissions = this.shiroSampleDao.getPermissionsByRole(role);
authorizationInfo.addStringPermissions(permissions);
});
return authorizationInfo;
}
}
```
这里用到了一个 DAO:shiroSampleDao。应该要通过它来从我们的数据库中获取用户、角色等信息。但是为了方便,我只是模拟了一下。
```java
package io.yorkecao.sample.dao;
import org.springframework.stereotype.Repository;
import java.util.*;
/**
* @author Yorke
*/
@Repository
public class ShiroSampleDao {
public Set getRolesByUsername(String username) {
Set roles = new HashSet<>();
switch (username) {
case "zhangsan":
roles.add("admin");
break;
case "lisi":
roles.add("guest");
break;
}
return roles;
}
public Set getPermissionsByRole(String role) {
Set permissions = new HashSet<>();
switch (role) {
case "admin":
permissions.add("read");
permissions.add("write");
break;
case "guest":
permissions.add("read");
break;
}
return permissions;
}
public String getPasswordByUsername(String username) {
switch (username) {
case "zhangsan":
return "zhangsan";
case "lisi":
return "lisi";
}
return null;
}
}
```
4. **实现 login、logout 接口**
在 Shiro 框架中实现登录、登出很容易。
这里我也提供了一个 read 和 write 的接口,这两个接口在 Service 实现。
```java
package io.yorkecao.sample.web;
import io.yorkecao.sample.service.ShiroSampleService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Yorke
*/
@RestController
public class ShiroSampleController {
@Autowired
private ShiroSampleService shiroSampleService;
@GetMapping("/login")
public void login(String username, String password) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
}
@GetMapping("/logout")
public void logout() {
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
}
@GetMapping("/read")
public String read() {
return this.shiroSampleService.read();
}
@GetMapping("/write")
public String write() {
return this.shiroSampleService.write();
}
}
```
5. **通过注解设置访问资源所需权限**
可以通过 `@RequiresPermissions` 等注解设置访问资源所需的权限。
```java
package io.yorkecao.sample.service;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Service;
/**
* @author Yorke
*/
@Service
public class ShiroSampleService {
@RequiresPermissions("read")
public String read() {
return "reading...";
}
@RequiresPermissions("write")
public String write() {
return "writting...";
}
}
```
## 测试
- 在登录之前,访问 `/read` 和 `/write` 接口都无效
- 用 lisi 登录(`GET http://localhost:8080/login?username=lisi&password=lisi`)后,可以访问 `/read`,不能访问 `/write`
- 用 zhangsan 登录(`GET http://localhost:8080/login?username=zhangsan&password=zhangsan`)后,`/read` 和 `/write` 都可以访问
- 登出后,访问 `/read` 和 `/write` 接口都无效