SpringBoot整合Shiro完成认证
2024-09-02 20:43:35
三、SpringBoot
整合Shiro
思路
首先从客户端发来的所有请求都经过Shiro
过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如果没有权限,访问失败;如果有权限访问成功。注意:客户端传来的token要和realm中的认证信息进行相同规则的比较(加密算法要一致)。
shiro常见的过滤器
SpringBoot
整合Shiro
实现认证
没有加密版
1、首先创建SpringBoot应用,勾选web模块
2、然后导入SpringBoot与shiro整合的依赖
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入SpringBoot与shiro整合的依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
3、创建Shiro的配置类。
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
// 创建ShiroFilterFactoryBean
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置受限资源,index是受限资源,authc
Map<String, String> map = new HashMap<String, String>();
// /**代表匹配所有url
map.put("/**", "authc");
// /user/login 是可以匿名访问的也就是公有资源
map.put("/user/login", "anon");
filterFactoryBean.setFilterChainDefinitionMap(map);
// 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
filterFactoryBean.setLoginUrl("/login.jsp");
return filterFactoryBean;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
@Bean
public Realm getRealm() {
CustomRelam customRelam = new CustomRelam();
return customRelam;
}
}
4、创建自定义realm。
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class CustomRelam extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
// 实际开发中 先确认传来的token用户名是否存在,如果存在,然后根据有户名查询密码返回,进而判断密码是否正确
// 这里假设从数据库中查到的用户名是 xiaozhang
String username = "xiaozhang";
if (username.equals(principal)) {
// 密码还没加密,下一步会加盐并加密
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,
"123", this.getName());
// 用户名和面都判断完成后可以返回
return simpleAuthenticationInfo;
}
return null;
}
}
5、编写controller,处理登录和退出的请求
package club.qy.datao.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author 胡
*/
@Controller
@RequestMapping("/user")
public class UserController {
// 退出操作
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login.jsp";
}
// 登录请求
@PostMapping("/login")
public String login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken passwordToken = new UsernamePasswordToken(username, password);
try {
subject.login(passwordToken);
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
System.out.println("用户名错误" + e.getMessage());
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误" + e.getMessage());
}
return "redirect:/login.jsp";
}
}
下面开始连接数据库MySQL
,MD5+salt
验证登录的认证
数据库表的创建
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL,
`password` varchar(40) DEFAULT NULL,
`salt` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
注册页面
<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1 class="">注册页</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
用户名:<input type="text" name="username"><br/>
密码: <input type="password" name="password"><br/>
<input type="submit" value="注册">
</form>
</body>
</html>
导入依赖
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入SpringBoot与shiro整合的依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
配置文件
server.port=8081
server.servlet.context-path=/shiro
spring.application.name=shiro
spring.mvc.view.suffix=.jsp
spring.mvc.view.prefix=/
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3309/shiro?characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
mybatis.type-aliases-package=club.qy.datao.entity
mybatis.mapper-locations=classpath:mapper/*.xml
创建实体类
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String username;
private String password;
private Integer id;
private String salt;
}
创建UserDao
@Repository
@Mapper
public interface UserDao {
void saveUser(User user);
}
UserDaoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="club.qy.datao.dao.UserDao">
<insert id="saveUser" parameterType="club.qy.datao.entity.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (#{id},#{username},#{password},#{salt})
</insert>
</mapper>
service实现
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void register(User user) {
//1。生成随机盐
String saltString = SaltUtil.getSaltString(9);
//2、给用户设置盐
user.setSalt(saltString);
//3、明文密码进行散列算法1024次,并加盐
Md5Hash md5Hash = new Md5Hash(user.getPassword(), saltString, 1024);
//4、将转成16进制的密码封装到user对象中
user.setPassword(md5Hash.toHex());
//5、插入数据库
userDao.saveUser(user);
}
}
SaltUtil工具类
public class SaltUtil {
public static String getSaltString(int n){
StringBuilder stringBuilder = new StringBuilder();
char[] array = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@~><?abcdefghijklmnopqrstuvwxyz".toCharArray();
for (int i = 0; i < n; i++) {
char ch = array[new Random().nextInt(array.length)];
stringBuilder.append(ch);
}
return stringBuilder.toString();
}
}
ShiroConfig类
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
// 创建ShiroFilterFactoryBean
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置受限资源,index是受限资源,authc
Map<String, String> map = new HashMap<String, String>();
// /**代表匹配所有url
map.put("/**", "authc");
// /user/login 是可以匿名访问的也就是公有资源
map.put("/user/**", "anon");
map.put("/register.jsp", "anon");
filterFactoryBean.setFilterChainDefinitionMap(map);
// 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
filterFactoryBean.setLoginUrl("/login.jsp");
return filterFactoryBean;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
@Bean
public Realm getRealm() {
CustomRelam customRelam = new CustomRelam();
return customRelam;
}
}
最新文章
- mono for android中使用dapper或petapoco对sqlite进行数据操作
- java中文乱码解决之道(三)-----编码详情:伟大的创想---Unicode编码
- BeautifulSoup的选择器
- Uva 11248 网络扩容
- 用WPF实现查找结果高亮显示
- struts1的ActionForm的作用域(生命周期)
- 访问WEB-INFO 目录注意事项
- php广告显示设置存放记录的目录代码
- runtime获取类名,遍历变量,遍历对象,遍历方法
- jQuery 其他操作
- 全国计算机等级考试二级教程-C语言程序设计_第12章_C语言中用户标识符的作用域和存储类
- oracle decode函数的用法
- python爬虫从入门到放弃(六)之 BeautifulSoup库的使用
- mybatis 详解(三)------入门实例(基于注解)
- Excel更改单元格格式后无效
- jsp页面集成xhEditor文本编辑器
- Maven相关命令
- Linux 系统TCP连接内存大小限制 调优
- javascript 高级程序设计 十
- CSS3 Flex布局整理(一)