目录
4.4编写转发控制器(不能直接访问页面,通过controller转发)
1、Shiro安全框架概念
Shiro是apache旗下的一个开源安全框架,它可以帮助我们完成身份认证,授权、加密、会话管理等功能。它有如下特点:
易于理解的API
简单的身份认证,支持多种数据源
简单的授权和鉴权
简单的加密API
支持缓存,以提升应用程序的性能
内置会话管理,适用于Web以及非Web的环境
不跟任何的框架或者容器捆绑,可以独立运行
认证
即系统判断用户的身份是否合法,合法可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录、二维码登录、手机短信登录、脸部识别认证、指纹认证等方式。认证是为了保护系统的隐私数据与资源,用户的身份合法才能访问该系统的资源。
授权
即认证通过后,根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。
2、Shrio模块组成
-
Authentication:身份认证/登录。
-
Authorization:权限验证,即判断用户是否能在系统中做某件事情。
-
Session Management:会话管理,用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中,会话可以是JavaSE环境的,也可以是Web环境的。
-
Cryptography:加密,保护数据的安全性。即密码加密存储到数据库,而不是明文存储。
-
Web Support:Web 支持,可以非常容易的集成到Web环境。
-
Caching:缓存。在用户登录后,用户信息、拥有的权限不必每次去查,这样可以提高效率。
-
Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。
-
Testing:提供测试支持。
-
Run As:允许一个用户假装为另一个用户的身份进行访问。
-
Remember Me:记住我,即一次登录后,下次再来就不用登录了。
3、Shrio核心组件
- Subject
主体。Subject
在Shiro中是一个接口,接口中定义了认证授权的相关方法。程序通过调用Subject
的方法进行认证授权,而Subject
使用SecurityManager
进行认证授权。
- SecurityManager
权限管理器,它是Shiro的核心。通过SecurityManager
可以完成具体的认证、授权等操作,SecurityManager
是通过Authenticator
进行认证,通过Authorizer
进行授权,通过SessionManager
进行会话管理。SecurityManager
是一个接口,继承了Authenticator
, Authorizer
, SessionManager
三个接口。
- Authenticator
认证器。对用户登录时进行身份认证
- Authorizer
授权器。用户认证通过后,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
- SessionManager
会话管理。shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上。
- Realm
领域。他是连接数据源+认证功能+授权功能的具体实现。SecurityManager
通过Realm
获取用户的身份和权限信息,并对用户进行认证和授权。
- SessionDAO
会话dao,是对会话进行操作的一套接口。它可以将session数据存储到数据库或缓存服务器中。
- CacheManager
缓存管理,将用户权限数据存储在缓存中,这样可以减少权限查询次数,提高性能。
- Cryptography
密码管理,Shiro提供了一套加密/解密的组件,方便开发。
4.配置文件引入
4.1导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!--Shrio和Spring整合包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
4.2编写配置文件
#端口
server:
port: 8080
#数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/myshiro?serverTimezon=UTC
username: root
password: root123
#日志格式
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
#mybatis-plus日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.3将前端代码导入
4.4编写转发控制器(不能直接访问页面,通过controller转发)
package com.example.shrio.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PageController {
@RequestMapping("/{page}")
public String page(@PathVariable String page){
return page;
}
// 忽略图标
@RequestMapping("/facvion.ico")
public void Ico(){}
}
4.5编写配置文件(shrio.ini)
#声明用户名
[users]
user=root
Controller层 :
package com.example.shrio.controller;
import com.example.shrio.service.LoginService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource;
@Controller
public class LoginController {
@Resource
private LoginService loginService;
@PostMapping("/login")
public String login(String username,String password){
return loginService.login(username,password);
}
}
Service层:
package com.example.shrio.service;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
/**
* 配置文件认证
* @param username 用户名
* @param password 密码
* @return 跳转到哪个页面
*/
//认证逻辑
public String login(String username, String password) {
// 1、获取SecurityManager工厂,读取配置文件
IniSecurityManagerFactory securityManagerFactory = new IniSecurityManagerFactory();
// 2、获取SecurityManager对象
SecurityManager securityManager = securityManagerFactory.getInstance();
// 3、将SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 4、获取subject对象
Subject subject = SecurityUtils.getSubject();
// 5、将前端传过来的用户名密码封装成shiro提供的身份对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
// 6、shiro认证
subject.login(token);
// 7、认证通过跳转到主页面
return "main";
} catch (AuthenticationException e) {
// 8、认证不通过跳转到失败页面
return "fail";
}
}
}
5、数据库认证
提交的表单:
<form class="form" action="/login2" method="post">
<input type="text" placeholder="用户名" name="username">
<input type="password" placeholder="密码" name="password">
<button type="submit" id="login-button">登录</button>
</form>
Controller层:
package com.example.shrio.controller;
import com.example.shrio.service.LoginService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource;
@Controller
public class LoginController {
@Resource
private LoginService loginService;
@PostMapping("/login2")
public String login2(String username,String password){
return loginService.login2(username, password);
}
}
Service层:
package com.example.shrio.service;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Service
public class LoginService {
//注入数据源
@Resource
private DataSource dataSource;
//认证逻辑
/**
* 数据库认证
* @param username 用户名
* @param password 密码
* @return 跳转到哪个页面
*/
public String login2(String username,String password){
// 1、获取SecurityManager对象
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 2、为SecurityManager对象设置Realm
JdbcRealm realm = new JdbcRealm();
realm.setDataSource(dataSource);
securityManager.setRealm(realm);
// 3、将SecurityManager添加到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 4、获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 5、将前端传来的用户名和密码封装成Shiro对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
// 6、shiro认证
subject.login(token);
// 7、认证通过
return "main";
}catch (AuthenticationException e){
// 8、认证失败
return "fail";
}
}
}
6、将Shiro对象交予容器管理
提交的表单:
<form class="form" action="/login3" method="post">
<input type="text" placeholder="用户名" name="username">
<input type="password" placeholder="密码" name="password">
<button type="submit" id="login-button">登录</button>
</form>
SecuirtyConfig配置类: 将SecurityManager交予Spring容器管理
package com.example.shrio.config;
import org.apache.ibatis.annotations.Param;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ScurityConfig {
//将JdbcRealm交予容器管理
@Bean
public JdbcRealm jdbcRealm(DataSource dataSource){
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
return jdbcRealm;
}
//将SecurityManager交予容器管理
@Bean
public DefaultWebSecurityManager securityManager(JdbcRealm jdbcRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(jdbcRealm);
return securityManager;
}
}
Controller层:
package com.example.shrio.controller;
import com.example.shrio.service.LoginService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource;
@Controller
public class LoginController {
@Resource
private LoginService loginService;
@PostMapping("/login3")
public String login3(String username,String password){
return loginService.login3(username,password);
}
}
Service层:
package com.example.shrio.service;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Service
public class LoginService {
@Resource
private DefaultWebSecurityManager securityManager;
/**
* 将Shiro交予容器管理
* @param username
* @param password
* @return
*/
public String login3(String username,String password){
// 1、将SecurityManager添加到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 2、获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 3、将前端传过来的用户名和密码封装成Shiro对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 4、shiro认证
try{
subject.login(token);
// 5、认证通过
return "main";
}catch (AuthenticationException exception){
// 6、认证失败
return "fail";
}
}
}