dependencies:
<!--
Subject 用户
SecurityManager 管理所有用户
Realm 连接数据
-->
<!--shrio整合spring的包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--thymeleaf 模板依赖 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<!--thymeleaf 整合 shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
thymleaf命名空间:
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> <!--thymeleaf整合shiro 的命名空间 -->
shiro 核心组件
Subject、SecurityManager、Realm
springboot项目环境搭建完毕后,对shiro进行配置(使用java配置类方式)
@Configuration
public class ShiroConfig {
//第三步 : ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/**
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有记住我功能才能使用
* perms:拥有对某个资源的权限才能访问
* role:属于某些角色才能访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
// 前端控制器(controller) /user下面的资源需要认证后才能访问,不然会跳转到登陆页面
//认证
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
//授权,正常的情况下跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
bean.setFilterChainDefinitionMap(filterMap);
//未登陆页面
bean.setLoginUrl("/toLogin");
//未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//第二部 : DefaultWebSecurityManager
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
//第一步 :创建Realm对象 ,需要自定义类
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect:用来整合shiro和thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
自定义Realm 、DefaultWebSecurityManager、ShiroFilterFactoryBean,并将其注入到spring 的IOC容器中,其中三个组件之间的引用是通过@Qualifier("")标签引用的IOC 容器中的对象
在ShiroFilterFactoryBean中指定页面访问所需要的权限信息
自定义Realm并继承AuthorizingRealm:
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm{
@Autowired
UserServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取当前登陆的对象
Subject subject = SecurityUtils.getSubject();
//获取认证方法中传入的登陆对象
User currentUser = (User) subject.getPrincipal();
//设置当前登陆对象的权限(它在数据库中对应的权限)
info.addStringPermission(currentUser.getPerms());
//添加session
Session session = subject.getSession();
session.setAttribute("loginUser",currentUser);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证doGetAuthenticationInfo");
//对传递进来的token进行格式的转换
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//在数据库中根据用户名查询是否存在该用户
User user = userService.queryUserByName(userToken.getUsername());
//判断用户是否存在
if (user==null){
return null; //抛出UnknownAccountException 异常
}
//密码认证,shiro做 把user传入principal中,则在授权方法中可以使用getPrincipal方法获取数据库中的user
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
//注意,shiro自动验证密码要传入数据库中存放的密码,shiro自动将改密码与用户登陆时的密码进行校验
}
}
Controller:
@Controller
public class IndexController {
@RequestMapping({"index","/"})
public String toIndex(Model model){
model.addAttribute("msg","hello shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "/login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登陆数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//记住我功能: token.setRememberMe(true);
try{
subject.login(token); //执行登陆方法,没有异常就OK了
//注销功能:subject.logout();
return "/index";
}catch(UnknownAccountException e){ //用户名不存在
model.addAttribute("msg","用户名不存在");
return "/login";
}catch(IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "/login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问此页面";
}
}
index:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> <!--thymeleaf整合shiro 的命名空间 -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<h3 th:text="${msg}"></h3>
<a href="/toLogin">登陆</a>
<hr>
<div shiro:hasPermission="user:add"> <!--使用shiroDialect 继承了shiro和thymeleaf,当用户没有user:add权限时,不显示里面的a标签 -->
<a href="/user/add">add</a><br>
</div>
<div shiro:hasPermission="user:update">
<a href="/user/update">update</a><br>
</div>
</body>
</html>
项目目录结构截图:
坑:
shiro的授权过程,即自定义realm中的授权方法是在用户访问需要权限的页面才会执行。