继续我们shiro系列博客相关的学习笔记,前几篇只是大体对shiro的基本概念、可以实现的基本功能、并通过官网提供的简单示例大体了解了一下shiro,感觉挺干的,不想再这样整理下去了,像是什么INI配置、Realm、拦截器机制、会话管理这些不再单独整理博客记录了,之后都通过代码的方式来体现并不断拓展;各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!
这一篇我们使用Springboot整合Shiro,从用户登录到验证、授权整个流程通过代码简单梳理一下。
目录
基础准备环境
首先我们引入相关依赖
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- SpringBoot集成 mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!-- SpringBoot集成thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.1</version>
</dependency>
<!-- Shiro使用Srping框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
数据库建表
CREATE TABLE t_user (
id int(11) NOT NULL AUTO_INCREMENT,
user_name varchar(36) DEFAULT NULL,
user_passwd varchar(36) DEFAULT NULL,
age int(11) DEFAULT NULL,
sex char(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
添加一条测试数据:
insert into t_user values(1,'yjh','admin',12,'1');
login.html index.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body >
<div>
<form method="post" th:action="@{/login}">
UserName:<input name="username" th:type="text">
PassWord:<input name="password" th:type="password">
Submit:<input type="submit" value="Submit" />
</form>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body >
登录成功!
</body>
</html>
登录验证
登录验证逻辑我们写在新加的LoginManager中,方便以后进行扩展时代码臃肿,这里只是简单进行了用户名和密码的验证,后续会不断完善,比如验证码验证,密码重试次数限制等。
@Component
public class LoginManager {
@Autowired
private UserManager userManager;
/**
* 登录验证
*/
public void login(String username, String password){
User user = userManager.findByUserName(username);
if(user == null){
throw new BusinessException("用户不存在",409);
}
if(!"123".equals(password)){
throw new BusinessException("用户名或密码错误",409);
}
}
}
实体类、yml配置、Mapper等就不贴代码了,下面用到的时候再补上。
正式的业务代码
LoginController:
@Controller
public class LoginController {
/**
* 跳转到登录页面
*/
@GetMapping("/login")
public String login() {
return "login";
}
/**
* 登录验证
*/
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password) {
// 1、收集主题提交的身份和凭证
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
// 2、提交主题的身份和凭证进行身份验证。
Subject subject = SecurityUtils.getSubject();
subject.login(token);
} catch (Exception e) {
System.out.println(e.getCause());
}
return "index";
}
}
配置下application.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/shiro?serverTimezone=UTC
username: root
password: root
thymeleaf:
mode: html
encoding: utf-8
# 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器;线上设置为true,可以提高性能。
cache: false
# 指定模板所在的目录
prefix: classpath:/
mybatis-plus:
configuration:
# # sql输出到控制台 方便查看
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注意上面配置thymeleaf模板所在的路径要准确,prefix默认会去classpath:/templates/ 目录下去找模板,从提示上即可看出:
如果配置错误,当你访问login页面时浏览器会报如下错误:
控制台会报如下错误:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [login], template might not exist or might not be accessible by any of the configured Template Resolvers
自定义Realm
由前面几篇对shiro的学习,我们知道认证是委托给SecurityManager来做的,而SecurityManager需要Realm来加载用户数据,下面我们定义一下我们自己的Realm进行认证和授权,下面只是进行登录认证:
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserManager userManager;
@Autowired
private LoginManager loginManager;
/**
* 认证: 登录时调用
* AuthenticationToken 用于收集用户提交的身份(如用户名)及凭据(如密码)
* Object getPrincipal() --- 身份
* Object getCredentials() --- 凭据
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken utoken = (UsernamePasswordToken) token;
// 登录验证逻辑
String username = utoken.getUsername();
String password = new String(utoken.getPassword());
loginManager.login(username,password);
// 如果身份认证验证成功,返回一个AuthenticationInfo实现,保存主体和凭据。
return new SimpleAuthenticationInfo(utoken.getUsername(),new String(utoken.getPassword()),getName());
}
/**
* 授权
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
配置拦截器
回想在使用SpringMvc的时候,通常都需要在web.xml中配置一个拦截器,然后在Spring的xml中配置拦截器的bean对象,比如像下面这样:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
</bean>
现在我们使用Springboot整合Shiro时也是一样的道理,需要在我们的配置文件中配置拦截器,拦截器可以看做是程序的入口,认证、授权等都需要SecurityManager,那少不了需要将SecurityManager注入到拦截器中,如下:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
return factoryBean;
}
如果不配置ShiroFilterFactoryBean,登录的时候会报错,错误信息如下:
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
像是UserManager还有Mapper这些,不贴代码了,就是一个简单的查询数据库的方法。
测试
到此,一个基本的Springboot 整合 Shiro的项目就完成了,包含登录和登录验证,并没有授权和权限的验证,下一篇继续完善。
访问 http://localhost:8080/login 进入到登录页面:
输入用户名密码登录后跳到成功页面:
输入错误的用户名或密码,因验证不通过停留在login页面。