本文内容大部分来自黑马视频的SpringBoot与Shiro整合-权限管理实战视频,在此记录为个人学习笔记。
可按步骤操作,无法实操的实战blog都是耍流氓。
三、shiro认证-ShiroConfig配置类
1. shiro的核心API
- Subject:用户主体(关联SecurityManager,把操作交给SecurityManager)
- SecurityManager:安全管理器(关联Realm)
- Realm:shiro连接数据库的桥梁
2. Spring整合shiro
(1)导入shiro和spring整合依赖
<!-- 导入shiro和spring整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
(2)自定义Realm类
package com.fukaiit.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
(3)编写shiro配置类(基本结构)
package com.fukaiit.shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* shiro的配置类
* @author Administrator
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getdefaultDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
四、shiro认证-使用shiro过滤器实现认证资源拦截
1. 在templates下新建user/add.html和user/update.html
2. 在UserController中编写访问这两个页面的方法
@RequestMapping("/add")
public String add() {
return "user/add";
}
@RequestMapping("/update")
public String update() {
return "user/update";
}
3. 在testThymeleaf.html页面中添加这两个页面的超链接
进入用户新增页面:<a href="add">用户新增</a>
进入用户更新页面:<a href="update">用户更新</a>
测试可访问。
4. 在getShiroFilterFactoryBean方法中添加拦截
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加shiro内置过滤器,实现权限相关的url拦截
/**
* 常见过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用Remember Me的功能,可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String, String> filterMap=new LinkedHashMap<String, String>();
filterMap.put("/add", "authc");
filterMap.put("/update", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
拦截之后重启,再次访问发现:试图访问add或者update,会自动跳转到login.jsp页面
5. 修改拦截后跳转的页面
(1)在templates下新增login.html页面
(2)在ShiroConfig中shiroFilterFactoryBean方法中修改拦截后跳转的页面
//修改跳转的登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
(3)在UserController中添加toLogin方法
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
测试,当没有权限跳转到该login.html页面
6. 试试用通配符拦截
filterMap.put("/testThymeleaf", "anon");
filterMap.put("/*", "authc");
五、shiro认证-实现用户登录功能
1. 修改完善之前的login.html页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
<span style="color:red" th:text="${msg}"></span>
<form method="post" action="login">
<input type="text" name="username" /><br>
<input type="password" name="password"><br>
<input type="submit" name="submit" value="登录">
</form>
</body>
</html>
2. 在UserController中编写/login请求,编写登录的处理逻辑
@RequestMapping("/login")
public String login(String usernam,String password,Model model) {
/**
* 使用shiro编写认证操作
*/
//获取Subject
Subject subject=SecurityUtils.getSubject();
//封装用户数据
UsernamePasswordToken token=new UsernamePasswordToken(usernam,password);
//执行登录方法
try {
//只要执行login方法,就会去执行UserRealm中的认证逻辑
subject.login(token);
//如果没有异常,代表登录成功
//跳转到textThymeleaf页面,代表主页
return "redirect:/testThymeleaf";
} catch (UnknownAccountException e) {
e.printStackTrace();
//登录失败
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
model.addAttribute("msg","密码错误");
return "login";
}
}
测试发现,并没有进入/login请求,是因为之前写的拦截器(/*)拦截了所有请求,再对/login请求放行filterMap.put("/login", "anon");
重启测试,发现执行了认证逻辑,返回了用户名不存在异常。
3. 在UserRealm中编写shiro认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//先写模拟数据进行验证,下一步再连接数据库,假设数据库的用户名和密码如下
String dbusername="fukaiit";
String dbpassword="123456";
//编写shiro判断逻辑,判断用户名和密码
//1. 判断用户名
UsernamePasswordToken token=(UsernamePasswordToken) arg0;
if (!token.getUsername().equals(dbusername)) {
//用户名不存在
return null;//shiro底层会抛出UnknownAccountException
}
//2. 判断密码
return new SimpleAuthenticationInfo("",dbpassword,"");//参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断
}
4. 启动程序,进行测试
六、shiro认证-整合MyBatis完善用户登录
1. 导入Mybatis相关的依赖
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Spring的MyBatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
2. 配置application.properties(src/main/resources下)
(1)创建用户表
-- 创建用户表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
PASSWORD VARCHAR(20)
);
(2)配置application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot-shiro
spring.datasource.username=root
spring.datasource.password=root
# 连接池配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# mybatis 别名扫描
mybatis.type-aliases-package=com.fukaiit.domain
3. 编写User实体类
package com.fukaiit.domain;
public class User {
private Integer id;
private String username;
private String password;
//getter and setter methods
}
4. 编写接口UserMapper.java(dao)
package com.fukaiit.mapper;
import com.fukaiit.domain.User;
public interface UserMapper {
public User findByUsername(String username);
}
5. 编写UserMapper.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="com.fukaiit.mapper.UserMapper">
<select id="findByUsername" parameterType="String" resultType="User" >
select id,username,password from user where username = #{value}
</select>
</mapper>
Tips:
1. xml的名字必须与接口文件的名字一致;
2. mapper中的namespace必须与接口类的全名一致
6. 编写UserService.java业务接口
package com.fukaiit.service;
import com.fukaiit.domain.User;
public interface UserService {
public User findByUsername(String username);
}
7. 编写UserServiceImpl.java业务实现类
package com.fukaiit.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fukaiit.domain.User;
import com.fukaiit.mapper.UserMapper;
import com.fukaiit.service.UserService;
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
}
8. 在启动类添加@MapperScan注解,开启Mybatis的Mapper接口扫描
@MapperScan("com.fukaiit.mapper")
9. 修改UserRealm.java,调用刚编写的业务
@Autowired
private UserService userService;
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码
//1. 判断用户名
UsernamePasswordToken token=(UsernamePasswordToken) arg0;
User user = userService.findByUsername(token.getUsername());
if (user==null) {
//用户名不存在
return null;//shiro底层会抛出UnknownAccountException
}
//2. 判断密码
return new SimpleAuthenticationInfo("",user.getPassword(),"");//参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断
}
10. run
效果同上节最后。