本文介绍 Spring Boot 2 集成 Apache Shiro 实现登录认证的方法。
目录
- Spring Boot 2 集成 Apache Shiro 方案简介
- 开发环境
- 基于
shiro-spring
和shiro-web
实现示例 - 基于
shiro-spring-boot-web-starter
实现示例 - 总结
Spring Boot 2 集成 Apache Shiro 方案简介
Spring Boot 2 集成 Apache Shiro 有以下两种方案:
- 基于
shiro-spring
和shiro-web
- 基于
shiro-spring-boot-web-starter
开发环境
- JDK 8
- Apache Maven 3.6.x
基于 shiro-spring
和 shiro-web
实现示例
创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程。
在
pom
文件中添加shiro-spring
和shiro-web
依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>tutorial.spring.boot</groupId>
<artifactId>spring-boot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-shiro</name>
<description>Spring Boot Shiro</description>
<properties>
<java.version>1.8</java.version>
<shiro.version>1.4.1</shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 自定义
Realm
实现。
package tutorial.spring.boot.shiro.config;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class CustomRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String name = (String) authenticationToken.getPrincipal();
if (!"shiro".equals(name)) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo(name, "123456", getName());
}
}
注意:使用 shiro
和 123456
作为示例用户名和密码,真实应用场景中需替换成其它方案。
- 创建 Shiro 配置类。
package tutorial.spring.boot.shiro.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public CustomRealm customRealm() {
return new CustomRealm();
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm());
return manager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorized");
// 配置路径拦截规则
Map<String, String> map = new LinkedHashMap<>();
map.put("/login/submit", "anon");
map.put("/**", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
}
注意:Shiro 配置类生成了一个 SecurityManager
实例,并使用 setRealm
方法配置了一个自定义 Realm
的实例。并生成一个 ShiroFilterFactoryBean
实例用于配置登录页、登录成功页、路径拦截规则等信息。
- 创建响应登录请求的控制器类。
package tutorial.spring.boot.shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/login")
public class LoginController {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
@GetMapping
public String login() {
return "Please login firstly!";
}
@PostMapping("/submit")
public String submit(String username, String password) {
AuthenticationToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
LOGGER.info("Authentication Pass");
return "Login successfully!";
} catch (UnknownAccountException e) {
LOGGER.warn("Authentication Reject: {}", e);
return "Login failed: unknown username!";
} catch (IncorrectCredentialsException e) {
LOGGER.warn("Authentication Reject: {}", e);
return "Login failed: incorrect password!";
} catch (LockedAccountException e) {
LOGGER.warn("Authentication Reject: {}", e);
return "Login failed: account has been locked!";
} catch (AuthenticationException e) {
LOGGER.error("Unexpected Error: {}", e);
return "Login failed!";
}
}
}
- 创建登录成功后响应首页请求的控制器类。
package tutorial.spring.boot.shiro.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("index")
public class IndexController {
@GetMapping
public String index() {
return "Index Page";
}
}
- 使用 Postman 测试。
首先,发起GET
请求localhost:8080/index
,返回提示【Please login firstly!】
其次,发起POST
请求localhost:8080/login/submit
,设置参数username
为shiro
,password
为123456
,返回登录成功提示【Login successfully!】
再次发起GET
请求localhost:8080/index
,返回提示【Index Page】
基于 shiro-spring-boot-web-starter
实现示例
在基于 shiro-spring
和 shiro-web
实现示例的基础上修改如下。
- 修改
pom
文件,去除shiro-web
和shiro-spring
依赖,引入shiro-spring-boot-web-starter
依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>tutorial.spring.boot</groupId>
<artifactId>spring-boot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-shiro</name>
<description>Spring Boot Shiro</description>
<properties>
<java.version>1.8</java.version>
<shiro.version>1.4.1</shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 修改配置文件
application.yml
,添加以下配置信息。
shiro:
web:
# 开启 Shiro
enabled: true
sessionManager:
# 允许将 sessionId 放入 cookie
sessionIdCookieEnabled: true
# 允许将 sessionId 放入 URL 地址栏
sessionIdUrlRewritingEnabled: true
loginUrl: /login
successUrl: /index
unauthorizedUrl: /unauthorized
无需修改自定义
Realm
实现。修改 Shiro 配置。
package tutorial.spring.boot.shiro.config;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public CustomRealm customRealm() {
return new CustomRealm();
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm());
return manager;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition shiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
shiroFilterChainDefinition.addPathDefinition("/login/submit", "anon");
shiroFilterChainDefinition.addPathDefinition("/**", "authc");
return shiroFilterChainDefinition;
}
}
注意:与之前示例不同之处在于,前一示例生成 org.apache.shiro.mgt.SecurityManager
和 org.apache.shiro.spring.web.ShiroFilterFactoryBean
实例,而此处生成的是 org.apache.shiro.web.mgt.DefaultWebSecurityManager
和 org.apache.shiro.spring.web.config.ShiroFilterChainDefinition
实例。
响应请求的控制器类无需修改。
测试方法同前一示例。
总结
本文仅仅简单介绍了 Spring Boot 2 集成 Apache Shiro 实现登录认证的基本方案实现,各项配置说明在后续文章中会详细介绍。