SpringBoot整合Shiro
详情:https://gitee.com/hale459/SpringBoot-Shiro
测试环境:
IDEA
SpringBoot 2.3.0
Shiro 1.5.3
项目构建
- 创建Springboot项目
- 引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
- thymeleaf整合Shiro依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 其他相关依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
编写Shiro配置类
- 创建一个自定义的Realm
/**
* 继承AuthorizingRealm 类并实现下列两个方法:
* doGetAuthorizationInfo:授权
* doGetAuthenticationInfo:认证
*/
public class UserReaml extends AuthorizingRealm {
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//当前登录的用户信息
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//从数据库中拿到当前用户的权限信息并设置
info.addStringPermission(currentUser.getPower());
return info;
}
/**
* 认证:
* 用户一旦登陆就会到此方法进行认证
* 这里全程密码是不可见的看不见明文密码
* 验证密码已经交给了shiro处理
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//用户的登录信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过数据库查询登录信息
User user = userService.login(token.getUsername());
//用户名认证:判断查询的信息是否为空
if (user == null) {
return null;
}
//将登陆信息放入session
SecurityUtils.getSubject().getSession().setAttribute("currentUser", user);
//密码认证:不需要用户做,shiro做(防止密码泄露)
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
}
- 新建一个Shiro的配置类
@Configuration
public class ShiroConfig {
//1.获取自定义Realm对象
@Bean
public UserReaml userReaml() {
return new UserReaml();
}
//2.绑定自定义的Realm对象到安全管理器
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(UserReaml userReaml) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//绑定自定义的Realm
securityManager.setRealm(userReaml);
return securityManager;
}
// 3.设置安全管理器:拦截
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
==添加shiro内置过滤器==
anon:无需认证可访问
authc:必须认证才能访问
user:必须拥有记住我功能才可使用
perms:拥有对某个资源的权限
role:拥有某个角色权限下能访问
*/
//配置拦截
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//设置用户有"user:page3"权限才能访问page3
filterChainDefinitionMap.put("/page3", "perms[user:page3]");
//设置只要登录了就可以访问的页面(所有人)
filterChainDefinitionMap.put("/page2", "authc");
filterChainDefinitionMap.put("/page1", "authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//到登陆页面
bean.setLoginUrl("/toLogin");
//登陆成功后跳转的页面
bean.setSuccessUrl("/index");
//没有授权时跳转到此页面
bean.setUnauthorizedUrl("/Unauthorized");
return bean;
}
//Thymeleaf整合Shiro
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
Entity类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
private Integer id;//用户id
private String username;//用户名
private String password;//密码
private String power;//权限
}
Controller类
@Controller
public class IndexController {
@Autowired
private UserService userService;
//跳转主页
@RequestMapping({"/", "/index", "/index.html"})
public String toIndex() {
return "index";
}
//跳转页面1
@RequestMapping("/page1")
public String page1() {
return "page1";
}
//跳转页面2
@RequestMapping("/page2")
public String page2() {
return "page2";
}
//跳转页面3
@RequestMapping("/page3")
public String page3() {
return "page3";
}
//跳转登陆页面
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
//登录校验
@PostMapping("/login")
public String login(String username, String password, Model model) {
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据到Token令牌中
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//设置记住我功能
//token.setRememberMe(true);
try {
//执行登录操作:如果没有异常就到主页,否则异常并返回到登录页
subject.login(token);
model.addAttribute("token", token);
model.addAttribute("msg", "你好," + token.getUsername() + "!");
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("errorInfo", "用户名错误!");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("errorInfo", "密码错误!");
return "login";
}
}
//未授权页面
@RequestMapping("/Unauthorized")
@ResponseBody
public String unauthorized() {
return "未授权无法访问!";
}
//退出登录
@RequestMapping("/loginOut")
public String loginOut() {
SecurityUtils.getSubject().logout();
return "login";
}
//注册页
@RequestMapping("/toReg")
public String toReg() {
return "reg";
}
//注册校验
@PostMapping("/reg")
@Transactional(rollbackFor = Exception.class)
public String reg(String username, String password, Model model) {
int reg = userService.reg(new User(null, username, password, null));
if (reg == 1) {
return "login";
} else {
model.addAttribute("error", "注册失败,请重试!");
return "reg";
}
}
}
Dao接口
public interface UserDao {
//登录校验
User login(String username);
//注册
int reg(User user);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lethe.dao.UserDao">
<sql id="queryAll">
select id,username,password,power from user
</sql>
<select id="login" parameterType="User" resultType="User">
<include refid="queryAll"/>
<where>
username=#{username}
</where>
</select>
<insert id="reg" parameterType="User">
insert into user values(null,#{username},#{password},null)
</insert>
</mapper>
Service接口
public interface UserService {
//登录校验
User login(String username);
//注册
int reg(User user);
}
Service接口实现
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public User login(String username) {
return userDao.login(username);
}
@Override
public int reg(User user) {
return userDao.reg(user);
}
}
全局配置
server:
port: 8080
spring:
profiles:
active: jdbc,logger
thymeleaf:
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html
suffix: .html
prefix: classpath:/templates/
cache: false
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.lethe.entity
logger
logging.level.root=info
logging.level.com.lethe.dao=debug
jdbc
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&seUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
测试
登录不同的用户,对不同的用户设置不同的权限即可;