SpringBoot整合shiro安全框架入门(一)
功能简介
这个入门案例,整合了最基本的shiro功能,包括对url的控制、对按钮的控制,其shiro标签内容可以参考另一篇博客thymeleaf模板+Shiro标签对按钮权限的控制
1、环境配置
(1)引入依赖 pom.xml
<!--引入shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--引入thymeleaf对shiro标签的支持-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
(2)配置 shiroconfig.class
tips:
这里说一下自己犯傻遇到的一个bug
这是正常的目录结构,此时的配置是正确的。前几天第一次接触shiro时,我的shiro相关配置不小心建在了application.class启动文件的上一层目录中了。即我在braisedpanda下新建了配置文件QAQ,结果一直报错,改了老半天才发现…
ShiroConfig.class
package com.braisedpanda.shirotest.shiro;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//这里设置没有登录时,跳转界面
shiroFilterFactoryBean.setLoginUrl("/");
//这里是没有授权时,跳转的界面
shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/userlist/**", "roles[dg]");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/layui/**", "anon");
filterChainDefinitionMap.put("/lib/**", "anon");
filterChainDefinitionMap.put("/student/delete/**","perms[delete]");
filterChainDefinitionMap.put("/student/toadd/**", "perms[create]");
filterChainDefinitionMap.put("/user/delete/**","perms[delete]");
filterChainDefinitionMap.put("/user/toadd/**", "perms[create]");
filterChainDefinitionMap.put("/student/toeditstudent/**", "perms[update]");
filterChainDefinitionMap.put("/admin/**", "authc");
filterChainDefinitionMap.put("/user/**", "authc");
//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(customRealm());
return defaultSecurityManager;
}
@Bean
public CustomRealm customRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//注解
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
这里说一下关键点:
a、这个不用多说,分别配置了没有登录时、没有授权时,跳转的界面
tips: 这里又有个坑
这是springboot自动生成的根文件地址。在配置shiroconfig时,不能写:
filterChainDefinitionMap.put("/static/**", "anon");
这样写它不认识!!!你要不就按照我这样写,每个文件夹,单独配置一下,要不就把images,layui,lib,page全部放进一个文件夹里,再配置这个文件夹也行。
filterChainDefinitionMap.put("/放进全部静态资源的文件夹/**", "anon");
b、配置需要拦截的url地址,后面可以配置需要的权限"perms[权限名称]“或者配置所需要的角色"roles[角色名称]”
c、最后一个拦截收尾
(3)配置自定义realm
package com.braisedpanda.shirotest.shiro;
import com.braisedpanda.shirotest.bean.Role;
import com.braisedpanda.shirotest.bean.User;
import com.braisedpanda.shirotest.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("-------开始权限认证--------");
// String username = (String) SecurityUtils.getSubject().getPrincipal();
User user = (User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//查询登录用户所拥有的角色,并添加角色
int uid = user.getUid();
List<String> roleList = userService.getRole(uid);
System.out.println("所拥有的角色:");
for (String s:
roleList) {
info.addRole(s);
System.out.println(s);
}
//查询登录用户所拥有的权限,并添加权限
List<String> permissionList = userService.getPermission(uid);
System.out.println("所拥有的权限");
for (String s:
permissionList) {
info.addStringPermission(s);
System.out.println(s);
}
return info;
}
//重写验证身份的方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("-------开始身份认证--------");
String username = (String) authenticationToken.getPrincipal();
String password = new String((char[]) authenticationToken.getCredentials());
User user = userService.getUser(username,password);
System.out.println("用户信息" + user);
if (user == null) {
throw new AccountException("用户名或密码错误");
}
return new SimpleAuthenticationInfo(user, password, getName());
}
}
(4)配置controller
登录的controller
//用户登录
@PostMapping("/login")
public ModelAndView login(String username, String password, HttpSession session){
ModelAndView modelAndView = new ModelAndView();
// 从SecurityUtils里边创建一个 subject
Subject subject = SecurityUtils.getSubject();
// 在认证提交前准备 token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 执行认证登陆
try {
subject.login(token);
} catch (UnknownAccountException uae) {
modelAndView.addObject("tips","*未知账户~");
modelAndView.setViewName("index");
return modelAndView;
} catch (IncorrectCredentialsException ice) {
modelAndView.addObject("tips","*密码不正确~");
modelAndView.setViewName("index");
return modelAndView;
} catch (LockedAccountException lae) {
modelAndView.addObject("tips","*账户已锁定~");
modelAndView.setViewName("index");
return modelAndView;
} catch (ExcessiveAttemptsException eae) {
modelAndView.addObject("tips","*用户名或密码错误次数过多~");
modelAndView.setViewName("index");
return modelAndView;
} catch (AuthenticationException ae) {
modelAndView.addObject("tips","*用户名或密码不正确~");
modelAndView.setViewName("index");
return modelAndView;
}
if (subject.isAuthenticated()) {
User user = userService.getUser(username,password);
session.setAttribute("user",user);
modelAndView.setViewName("menu/main");
return modelAndView;
} else {
token.clear();
modelAndView.addObject("tips","*未知账户~");
modelAndView.setViewName("index");
return modelAndView;
}
}
退出的controller
//退出登录
@RequestMapping("/quite")
public String quite(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "redirect:/";
}
(5)数据库表
这里附上相关的数据库表,方便学习理解
role表
permission表
user_role表(根据uid可查所有的role)
user_permission(根据uid可查所有对应的permission)
user表
2、前端演示
首先登录学生
仅显示了权限为retrieve的按钮
登录zhangsan
最后放下shiro标签的代码:
<shiro:hasPermission name="retrieve">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail" >查看</a>
</shiro:hasPermission>
<shiro:hasPermission name="update">
<a class="layui-btn layui-btn-xs" lay-event="edit" id="edit">编辑</a>
</shiro:hasPermission>
<shiro:hasPermission name="delete">
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</shiro:hasPermission>
标签内容参考另一篇博客thymeleaf模板+Shiro标签对按钮权限的控制
好啦,结束了~