使用SpringBoot2.0搭建企業級應用開發框架(七)整合Shiro
阿新 • • 發佈:2018-11-09
-
準備
首先建立使用者許可權表
//使用者表 CREATE TABLE `sys_user` ( `id` varchar(32) NOT NULL COMMENT 'id', `username` varchar(64) DEFAULT NULL COMMENT '使用者名稱', `password` varchar(64) DEFAULT NULL COMMENT '密碼', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='使用者表'; INSERT INTO `demo`.`sys_user` (`id`, `username`, `password`) VALUES ('1', 'admin', '123456'); //角色表 CREATE TABLE `sys_role` ( `id` varchar(32) NOT NULL COMMENT 'id', `role_name` varchar(32) DEFAULT NULL COMMENT '角色名稱', `role_desc` varchar(32) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表'; INSERT INTO `demo`.`sys_role` (`id`, `role_name`, `role_desc`) VALUES ('1', 'ROLE_ADMIN', '管理員'); //使用者-角色表 CREATE TABLE `sys_user_role` ( `id` varchar(32) NOT NULL COMMENT 'id', `user_id` varchar(32) DEFAULT NULL COMMENT '使用者id', `role_id` varchar(32) DEFAULT NULL COMMENT '角色id', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='使用者角色表'; INSERT INTO `demo`.`sys_user_role` (`id`, `user_id`, `role_id`) VALUES ('1', '1', '1');
然後用之前說的generator生成實體
-
整合
首先新增Shiro的依賴
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
然後建立自定義Realm
public class MyRealm extends AuthorizingRealm { @Autowired private SysUserMapper sysUserMapper; //認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //獲得當前使用者的使用者名稱 String username = (String) authenticationToken.getPrincipal(); //從資料庫中根據使用者名稱查詢使用者 SysUser user = sysUserMapper.findByUserName(username); if (user == null) { log.info("沒有使用者名稱為{}的使用者",username); throw new UnknownAccountException("沒有在本系統中找到對應的使用者資訊"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName()); return info; } //授權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //從憑證中獲得使用者名稱 String username = (String) SecurityUtils.getSubject().getPrincipal(); //根據使用者名稱查詢使用者物件 SysUser user = sysUserMapper.findByUserName(username); //查詢使用者擁有的角色 List<SysRole> roleList = user.getRoles(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); log.debug("使用者{}擁有以下角色:"); for (SysRole role : roleList) { //賦予使用者角色 info.addStringPermission(role.getRoleName()); } return info; } }
建立config類
@Configuration public class ShiroConfiguration { @Bean public MyRealm realm(){ return new MyRealm(); } //使用shiro-spring-boot-starter 1.4時,返回型別是SecurityManager會報錯,直接引用shiro-spring則不報錯 @Bean public DefaultWebSecurityManager securityManager(MyRealm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設定 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登入成功後要跳轉的連結 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授權介面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //攔截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出過濾器,其中的具體的退出程式碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/logout", "logout"); // 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 :這是一個坑呢,一不小心程式碼就不好使了; // authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/login.html", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }
建立登入測試
@Controller
public class ShiroController {
@GetMapping("/login")
public String login(){
return "redirect:login.html";
}
@PostMapping("/login")
public String login(String username, String password){
Subject user = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
String returnUrl = "redirect:index.html";
String a = "1";
try {
//shiro幫我們匹配密碼什麼的,我們只需要把東西傳給它,它會根據我們在UserRealm裡認證方法設定的來驗證
user.login(token);
} catch (UnknownAccountException e) {
//賬號不存在
returnUrl = "redirect:login.html?error=0";
//賬號不存在和下面密碼錯誤一般都合併為一個賬號或密碼錯誤,這樣可以增加暴力破解難度
} catch (DisabledAccountException e) {
//賬號未啟用
returnUrl = "redirect:login.html?error=1";
} catch (IncorrectCredentialsException e) {
//密碼錯誤
returnUrl = "redirect:login.html?error=2";
} catch (Throwable e) {
//未知錯誤
returnUrl = "redirect:login.html?error=3";
} finally {
return returnUrl;
}
}
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title1</title>
</head>
<body>
<form method="post" action="/login">
使用者名稱:<input name="username" /><br />
密碼:<input name="password" /><br />
<input type="submit" value="登入" />
</form>
</body>
</html>
整合中的坑
1、config類中的securityManager()返回型別
如果jar包用spring-shiro,則返回SecurityManager即可,如果用shiro-spring-boot-starter 1.4,則需要返回DefaultWebSecurityManager,否則會報錯
2、多環境配置
多環境配置中需要手動定義resuources目錄下載入的檔案,所以不要忘記將html加入進去
3、頁面跳轉問題
如果使用return "xx.html"方式的話,在登陸時,由於提交表單是post,而訪問xx.html是get方法,所以會報錯,需要使用redirect或者forward