spring boot 集成shiro
1、建表sql
/*
Navicat MySQL Data Transfer
Source Server : huamao
Source Server Version : 50540
Source Host : localhost:3306
Source Database : shiro
Target Server Type : MYSQL
Target Server Version : 50540
File Encoding : 65001
Date: 2018-09-28 09:44:50
*/
SET FOREIGN_KEY_CHECKS=0;
-- Table structure for sys_menu
DROP TABLE IF EXISTS sys_menu
;
CREATE TABLE sys_menu
(
menu_id
int(11) NOT NULL AUTO_INCREMENT COMMENT '按钮id',
menu_name
varchar(255) NOT NULL COMMENT '按钮名称',
PRIMARY KEY (menu_id
)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- Table structure for sys_role
DROP TABLE IF EXISTS sys_role
;
CREATE TABLE sys_role
(
role_id
int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
role_name
varchar(255) NOT NULL COMMENT '角色名称c',
PRIMARY KEY (role_id
)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- Table structure for sys_role_menu
DROP TABLE IF EXISTS sys_role_menu
;
CREATE TABLE sys_role_menu
(
id
int(11) NOT NULL AUTO_INCREMENT,
role_id
int(11) NOT NULL COMMENT '角色id',
menu_id
int(11) NOT NULL COMMENT '按钮id',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- Table structure for sys_user
DROP TABLE IF EXISTS sys_user
;
CREATE TABLE sys_user
(
user_id
int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
pass_word
varchar(255) NOT NULL COMMENT '登录密码',
user_name
varchar(255) NOT NULL COMMENT '用户名称',
PRIMARY KEY (user_id
)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- Table structure for sys_user_role
DROP TABLE IF EXISTS sys_user_role
;
CREATE TABLE sys_user_role
(
id
int(11) NOT NULL AUTO_INCREMENT,
role_id
int(11) NOT NULL COMMENT '角色Id',
user_id
int(11) NOT NULL,
PRIMARY KEY (id
)
2、引入相关jar包(使用的是thymeleaf)
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
3、ShiroConfig配置类
@Configuration
public class ShiroConfig {
//用于thymeleaf模板使用shiro标签
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl("/notLogin");
// 设置无权限时跳转的 url;
shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
// 设置拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置不被拦截的资源及链接
filterChainDefinitionMap.put("/static/**", "anon");
// 退出过滤器
filterChainDefinitionMap.put("/logout", "logout");
//配置需要认证权限的
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login"页面,即本文使用的login.html
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注入 securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(customRealm());
return securityManager;
}
/**
* 自定义身份认证 realm;
* <p>
* 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm,
* 否则会影响 CustomRealm类 中其他类的依赖注入
*/
@Bean
public CustomRealm customRealm() {
return new CustomRealm();
}
//开启shiro aop注解支持,不开启的话权限验证就会失效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
//配置异常处理,不配置的话没有权限后台报错,前台不会跳转到403页面
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
mappings.setProperty("UnauthorizedException","test/403");
simpleMappingExceptionResolver.setExceptionMappings(mappings); // None by default
simpleMappingExceptionResolver.setDefaultErrorView("error"); // No default
simpleMappingExceptionResolver.setExceptionAttribute("ex"); // Default is "exception"
return simpleMappingExceptionResolver;
}
}
4、实现CustomRealm
public class CustomRealm extends AuthorizingRealm {
@Autowired
private ISysUserService sysUserService;
/**
* 获取授权信息
*只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("————权限认证————");
// String userName = JWTUtil.getUsername(principalCollection.toString());
String userName = (String) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获得该用户名称
Map<String,Object> map = new HashMap<String, Object>();
map.put("userName",userName);
//设置按钮级别的权限
List<String> list = sysUserService.selectMenuName(map);
Set<String> set = new HashSet<>(list);
info.setStringPermissions(set);
//设置角色级别的群贤
List<String> listRoleName = sysUserService.selectRoleName(map);
Set<String> setRoleName = new HashSet<>(listRoleName);
info.setRoles(setRoleName);
return info;
}
/**
* 获取身份验证信息
* Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。
*
* @param authenticationToken 用户身份信息 token
* @return 返回封装了用户信息的 AuthenticationInfo 实例
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("————身份认证方法————");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//String token = (String) authenticationToken.getCredentials();
// 解密获得username,用于和数据库进行对比
//String username = JWTUtil.getUsername(token);
// 从数据库获取对应用户名密码的用户
SysUser sysUser = new SysUser();
sysUser.setUserName(token.getUsername());
//user.setUsername(username);
EntityWrapper<SysUser> entityWrapper = new EntityWrapper<SysUser>(sysUser);
sysUser = sysUserService.selectOne(entityWrapper);
//String password = userMapper.getPassword(token.getUsername());
if (null == sysUser) {
throw new AccountException("用户名不正确");
} else if (!sysUser.getPassWord().equals(new String((char[]) token.getPassword()))){
throw new AccountException("密码不正确");
}
return new SimpleAuthenticationInfo(token.getPrincipal(), sysUser.getPassWord(), getName());
}
}
5、跳转页面权限测试(角色方面的权限与按钮方面的权限)
@Controller
public class TestController {
@Autowired
private ISysUserService sysUserService;
@GetMapping({"/","/index"})
public String index(){
return"test/index";
}
@GetMapping("/403")
public String unauthorizedRole(){
return "test/403";
}
@GetMapping("/delete")
@RequiresPermissions("delete")
public String delete(){
return "test/delete";
}
@GetMapping("/select")
@RequiresPermissions("select")
public String select(){
return "test/select";
}
@RequestMapping("/admin")
@RequiresRoles("admin")
public String damin(){
return "test/admin";
}
@RequestMapping("/user")
@RequiresRoles("user")
public String user(){
return "test/user";
}
@RequestMapping("/test")
public String test(){
return "test";
}
@RequestMapping("/login")
public String login(HttpServletRequest request, Map<String, Object> map) throws Exception{
System.out.println("HomeController.login()");
// 登录失败从request中获取shiro处理的异常信息。
// shiroLoginFailure:就是shiro异常类的全类名.
String exception = (String) request.getAttribute("shiroLoginFailure");
String msg = "";
//根据异常判断错误类型
if (exception != null) {
if (UnknownAccountException.class.getName().equals(exception)) {
msg = "账号不存在";
} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
msg = "密码不正确";
} else {
msg = "else >> "+exception;
}
}
map.put("msg", msg);
// 此方法不处理登录成功,由shiro进行处理
return "test/login";
}
@GetMapping("/logout")
public String logout(){
return "test/login";
}
}
6、页面的权限
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
index
<shiro:hasRole name="admin">
admin
</shiro:hasRole>
<shiro:hasRole name="user">
user
</shiro:hasRole>
<br/>
<form th:action="@{/logout}" method="post">
<p><input type="submit" value="注销"/></p>
</form>
</body>
</html>