目录
前言
1.介绍
2.整合 Shiro 到 Spring Boot
3.Shiro 相关配置
总结
前言
几乎所有涉及用户的系统都需要进行权限管理,权限管理涉及到一个系统的安全。Spring Boot 的安全框架整合方案中还有一个璀璨的明珠:Shrio。
1.介绍
Shiro是一款由Java 编写的安全框架,功能强大,入手容易。Shiro 提供了一套完的RABC模式的授权认证体系,可以对密码进行加密,并完成安全的会话管理。与SpringSecurity 相比显得功能较少,但是对于追求“小而美”的解决方案的开发者和项目来说Shiro使用起来更加得心应手。
- 用于身份验证以及登录,检查用户是否拥有相应的角色权限。
- 进行权限验证,验证某个已登录认证的用户是否拥有某个具体的角色权限; 常的如:检验某个用户是否有对某些资源包括页面的访问和操作权限等。
- 进行会话管理,每当用户登录就是一次会话,在没有退出账号登录之前,用户的所有信息都在会话中存储。
- 对数据加密,保护数据的安全性,如密码加密存储到数据库,不是明文存储,更加安全。
- 对Web 支持,非常方便地集成到 Web 环境中
- 支持多线程并发验证。
这里介绍 Shiro 的一些核心的概念,Shiro 主要由三部分组成:
- Subject: 主体,外部应用会和 Subject 进行交互。Subject 会记录当前的用户,用在这里就是 Subject (主体),比如通过浏览器进行请求的用户。而 Subject 要通过 SecurityManager 进行认证授权。在代码层面,Subject 是一个定义了一些授权方法的接口 。
- Security Manager: 即安全管理器,它是 Shiro 的核心,将对所有的 Subject 进行安全管理。从代码层面上来说,Security Manager 是一个多继承接口,继承了Authenticator、Authorizer、SessionManager 这三个接口。
- Realm:是 Shiro 和安全应用之间的连接器,类似于一个安全相关的 DAO,在进行认证和授权时,Shiro 会从 Realm 中获取想要的数据
2.整合 Shiro 到 Spring Boot
新建一个 SpringBoot 项目 ,在 pom.xml 中添加如下配置:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.7.0</version>
</dependency>
3.Shiro 相关配置
在 applcation.yml 中编写相关配置。
shiro:
# 开启 Shrio 配置,默认为 true
enabled: true
web:
#开启 Shrio Web 配置,默认为 true
enabled: true
#配置登录地址,默认为"login.jsp"
loginUrl: /login
#配置登录成功地址 默认为 /
successUrl: /index
# 配置未获取授权默认跳转地址
unauthorizedUrl: /unauthorized
sessionManager:
# 是否允许通过 Cookie,实现会话跟踪,默认为 true。
sessionIdCookieEnabled: true
#是否允许通过 URL 参数实现会话跟踪,默认为 true,如果网站支持 Cookie,可以关闭此选项
# thymeleaf
spring:
thymeleaf:
prefix: classpath:templates/
suffix: .html
mode: HTML
encoding: UTF-8
cache: false # 对于开发,最好禁用缓存
编写 ShiroConfig 文件,具体代码如下:
package org.example.config;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.realm.text.TextConfigurationRealm;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm(){
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("freephp=123456,user\n admin=123456,admin");
realm.setRoleDefinitions("user=read\n admin=read,write");
return realm;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/logout","logout");
chainDefinition.addPathDefinition("/login","anon");//匿名访问
chainDefinition.addPathDefinition("/doLogin","anon");//匿名访问
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
}
}
上面的代码中有两个方法,一个是 realm 方法,另一个是 shiroFilterChainDefinition 方法。realm 方法用于获取权限认证数据,例如此处存储了两个账号:freephp 和 admin。
然后再编写 Controller 文件,只做简单的逻辑判断,代码如下:
package org.example.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
@RequestMapping("/doLogin")
public String doLogin(String username, String password, Model model){
System.out.println("userName is"+username);
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return "index";
}catch (AuthenticationException e){
System.out.println(e.getCause());
model.addAttribute("error","Username or Password is wrong!");
return "login";
}
}
@GetMapping("/admin")
public String admin(){
return "admin";
}
@GetMapping("/user")
public String user(){
return "user";
}
}
上面的代码定义了三个接口,其中 doLogin 用于登录,使用 UsernamePasswordToken 类创建 token。然后根据账号和密码进行匹配判断,如果验证失败则返回 /dologin 页面并显示错误提示,如果验证成功则可以访问 index 页面。
登录页面和首页页面都需要单独编写,在 resources 目录下创建 templates 文件夹,然后分别创建 index.html 和 login.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hi,test
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form action="/doLogin" method="post">
<label>username:</label>
<input type="text" name="username"><br/>
<label>password:</label>
<input type="text" name="password"><br/>
<div th:text>${error}</div>
<input type="submit" value="登录"/>
</form>
</body>
</html>
为了更好的加载上面的页面,编写一个 WebMvcConfig 来加载:
package org.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("index").setViewName("index");
registry.addViewController("login").setViewName("login");
}
}
访问之后,输入正确的账号和密码,则可以看到登录成功的页面,反之则提示登录失败。
总结
Shiro 的使用非常方便,只需实现最核心的 realm 定义和 shiroFilterChainDefinition 功能就可以很好地完成认证授权功能。除此之外,Shiro 还提供缓存功能,感兴趣的同学可以自行查阅官方文档进行学习。