个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~
个人主页:.29.的博客
学习社区:进去逛一逛~
⑤【Shiro】SpringBoot整合Shiro,实现登录认证
- 目录结构
- Maven坐标导入
- 准备用于登录校验的数据
- 数据库表对应的实体类
- SpringBoot配置文件
- MyBatis-Plus操作数据库的Mapper接口
- Service层,提供一个根据name查询用户的业务方法
- 自定义Realm,实现自定义登录认证
- 控制层,使用Shiro进行登录认证
- Shiro配置类,使自定义Realm生效,设置拦截范围
- 测试登录认证功能
目录结构
目录结构
:
Maven坐标导入
pom.xml
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
准备用于登录校验的数据
创建表结构,插入数据
:
CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
USE `shirodb`;
CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
`pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
`rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
# e8e2ea5deb7e981462ab88c2b7e3f19a 是123456789经过MD5加盐三次机密后的结构,加的盐为"salt"
insert into user values(1, '.29.', 'e8e2ea5deb7e981462ab88c2b7e3f19a', 1);
数据库表对应的实体类
User
:
/**
* @author .29.
* @create 2024-03-17 10:53
*/
//lombok组件注解,提供构造器以及Getter、Setter
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
private Integer rid;
}
SpringBoot配置文件
applicaition.yml
:
# mybatis-plus配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
spring:
# 数据库连接配置
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shirodb?characterEncoding=utf-8&useSSL=false
username: root
password: abc123
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
MyBatis-Plus操作数据库的Mapper接口
UserMapper
:
/**
* @author .29.
* @create 2024-03-17 10:55
@Repository注解用于标识一个类作为数据访问层(DAO)的组件,类似于@Component,但可以将底层数据访问技术(如 JDBC、Hibernate 等)所抛出的异常转换为 Spring 的数据访问异常(DataAccessException),这样在上层代码中可以统一处理数据访问异常。
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
}
Service层,提供一个根据name查询用户的业务方法
Service接口
:
/**
* @author .29.
* @create 2024-03-17 10:56
*/
public interface UserService {
//用户登录
User getUserInfoByName(String name);
}
Service实现类
:
/**
* @author .29.
* @create 2024-03-17 10:57
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 根据name查询用户的业务方法
* @param name
* @return
*/
@Override
public User getUserInfoByName(String name) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", name);
User user = userMapper.selectOne(queryWrapper);
return user;
}
}
自定义Realm,实现自定义登录认证
MyRealm
:
/**
* @author .29.
* @create 2024-03-17 11:01
*/
@Configuration
public class MyRealm extends AuthorizingRealm {//自定义Realm需要继承AuthorizingRealm
@Autowired
private UserService userService;
/**
* 自定义授权方法,这里暂时不需要设置,直接返回null
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 自定义登录认证方法
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从token中获取用户身份信息
String name = authenticationToken.getPrincipal().toString();
//查询数据库中的用户信息
User user = userService.getUserInfoByName(name);
//判断并将数据封装进进行登录认证的对象,进行返回
if(user != null && name.equals(user.getName())){
//创建实现了校验逻辑的对象,传入校验数据,交给shiro底层进行认证
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
authenticationToken.getPrincipal()
, user.getPwd()
, ByteSource.Util.bytes("salt")
, name
);
return info;
}
return null;
}
}
控制层,使用Shiro进行登录认证
myController
:
/**
* @author .29.
* @create 2024-03-17 11:27
*/
@Controller
@RequestMapping("/myController")
public class MyController {
@GetMapping("/userLogin")
@ResponseBody
public String userLogin(String name, String pwd){
//获取subject对象
Subject subject = SecurityUtils.getSubject();
//封装请求数据至token
UsernamePasswordToken token = new UsernamePasswordToken(name, pwd);
//调用login方法进行登录认证
try{
subject.login(token);
System.out.println("登陆成功!");
return "登陆成功";
}catch (AuthenticationException e){
e.printStackTrace();
System.out.println("登陆失败!");
return "登陆失败";
}
}
}
Shiro配置类,使自定义Realm生效,设置拦截范围
ShiroConfig
:
/**
* @author .29.
* @create 2024-03-17 11:14
*/
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm myRealm;
//配置SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//创建DefaultWebSecurityManager对象(安全管理器)
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//创建加密对象,设置属性
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //使用什么加密算法?
hashedCredentialsMatcher.setHashIterations(3); //循环加密几次?
myRealm.setCredentialsMatcher(hashedCredentialsMatcher); //为自定义Realm设置加密对象
defaultWebSecurityManager.setRealm(myRealm); //将自定义Realm存入安全管理器
return defaultWebSecurityManager; //返回安全管理器
}
//配置Shiro内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition(){
//创建拦截器定义对象
DefaultShiroFilterChainDefinition filter = new DefaultShiroFilterChainDefinition();
//配置不认证也能访问的资源(参数anon代表无需认证)
filter.addPathDefinition("/myController/userLogin","anon");
filter.addPathDefinition("/login","anon");
//配置需要认证访问的资源(参数authc代表需要认证)
filter.addPathDefinition("/**","authc");
return filter;
}
测试登录认证功能
启动项目,访问地址传递参数
: