Shiro的三個核心組件:Subject,SecurityManager和Realmsjavascript
Subject:指的是當前操做的用戶,能夠是人,能夠是機器或者第三方的進程,與軟件交互的東西。SubjectManager:是shiro框架的核心shiro經過SubjectManager來管理內部組件的實例,並提供安全管理的各類服務。Realm:Realm充當shiro與安全數據之間的橋樑,簡單地說就是當用戶執行認證(登陸)受權(訪問控制)會從Realm中查找用戶及其權限信息,配置Realm能夠是一個也能夠是多個。html
Spring Boot集成shiro添加shiro依賴包java
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
配置Shirojquery
@Configuration public class ShiroConfig { //配置一個自定義的Realm 最後將這個bean返回到 完成認證及受權的具體對象 @Bean public Realm myRealm(){ return new MyRealm(); } @Bean public SecurityManager securityManager(Realm myRealm){ //設置一個Realm 這個Realm最終這個Ream是咱們最終完成認證及受權的具體對象 DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm); return defaultWebSecurityManager; } //將shiro的過濾器定義到spring的上下文中 利用這個類來完成對請求的各類攔截 @Bean public ShiroFilterFactoryBean filterFactoryBean(SecurityManager securityManager){ //建立攔截器用於攔截咱們的用戶請求 ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); //設置shiro的安全管理 設置管理時會指定某個Realm來完成權限的分配 factoryBean.setSecurityManager(securityManager); factoryBean.setLoginUrl("/");//設置沒有登陸後的跳轉頁 factoryBean.setSuccessUrl("/success");//設置登陸成功後 factoryBean.setUnauthorizedUrl("/noperms");//沒有權限的跳轉頁面 LinkedHashMap<String,String> chain = new LinkedHashMap(); /*登出請求用於shiro清空會話 自動返回到未登陸的頁面*/ chain.put("/logout","logout"); //以admin開頭的請求必須登陸後才能訪問 chain.put("/login","anon");//用戶登陸請求不須要登陸就能訪問 //roles[admin] 用於指定以admin開頭的請求必須有admin 的角色才能訪問 chain.put("/admin/**","authc,roles[admin]"); chain.put("/user/**","authc,roles[user]"); chain.put("/js/**","anon"); chain.put("/salt","anon"); chain.put("/**","authc"); factoryBean.setFilterChainDefinitionMap(chain);//不是普通的map集合 return factoryBean; } }
配置MyReam類**AuthenticatingRealm 專門用於完成身分驗證的抽象父類*AuthorizingRealm 完成身份驗證 及權限分配web
public class MyRealm extends AuthorizingRealm {
//用戶身份認證的具體方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
/**
* Shiro 的認證方法咱們須要在這個方法中來獲取用戶的信息(從數據庫中)
* @param authenticationToken 用戶登陸時的 Token(令牌),這個對象中將存放着咱們用戶在瀏覽器中存放的帳號和密碼
* @return 返回一個 AuthenticationInfo 對象,這個返回之後 Shiro 會調用這個對象中的一些方法來完成對密碼的驗證 密碼是由 Shiro進行驗證是否合法
* @throws AuthenticationException 若是認證失敗 Shiro 就會拋出AuthenticationException
* 咱們也能夠手動本身拋出這個 AuthenticationException以及它的任意子異常類不通的異常類型能夠認證過程當中的不通錯誤狀況咱們須要根據
* 異常類型來爲用戶返回特定的響應數據
* AuthenticationException 異常的子類 能夠咱們本身拋出
* AccountException 帳號異常 能夠咱們本身拋出
* UnknownAccountException 帳號不存在的異常 能夠咱們本身拋出
* LockedAccountException 帳號異常鎖定異常 能夠咱們本身拋出
* IncorrectCredentialsException 密碼錯誤異常 這個異常會在 Shiro 進行
密碼驗證是拋出
*/
//獲取用戶的身份令牌
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();//獲取用戶在頁面中輸入的帳號
if (!"admin".equals(username) && !"zxp".equals(username) && !"lishi".equals(username)){
throw new UnknownAccountException("帳號輸入錯誤");
}else if ("zxp".equals(username)){
throw new LockedAccountException("帳號凍結");
}
Subject subject = SecurityUtils.getSubject();
//用session來獲取隨機鹽
String randomsalt = (String) subject.getSession().getAttribute("randonsalt");
String dbpassword = "e10adc3949ba59abbe56e057f20f883e";
//使用隨機鹽對數據庫中的密碼進行加密
String password = new SimpleHash("MD5",dbpassword,randomsalt).toString();
//返回驗證密碼的對象
return new SimpleAuthenticationInfo(username,password,this.getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取觸發權限認證的當前用戶帳號
String username = (String) principalCollection.getPrimaryPrincipal();
//根據帳號來作判斷 設置權限
Set<String> roles = new HashSet<>();
if ("admin".equals(username)){
roles.add("admin");
roles.add("user");
}else if ("lishi".equals(username)){
roles.add("user");
}
SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
simpleAuthenticationInfo.setRoles(roles);
return simpleAuthenticationInfo;
}
}
TestControllerspring
@Controller public class testcontroller { @RequestMapping("/") public String index(){ return "index"; } @RequestMapping("/login") public String login(String username, String password, Model model){ //獲取當前的操做對象 能夠是人 也能夠是第三方 Subject subject = SecurityUtils.getSubject(); //定義一個token 用於存放用戶登陸的帳號密碼 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password); //判斷當前用戶是否完成認正登陸若是若是寫了這個if用戶沒法重複登陸 在shiro中一旦用戶登陸後 shiro會將用戶的信記錄到subject中故 isAuthenticated() 返回的是TRUE /* *讓用戶能夠重複登陸 * 1.不寫if 但會增長服務器的壓力 * 2.調用subject中的logout用於退出當前的登陸狀態 清空會話中的全部數據 * 3.在頁面中編寫一個用戶安全退出的請求 * */ if (subject.isAuthenticated()){ return "success"; } try { subject.login(usernamePasswordToken); }catch (UnknownAccountException e){ model.addAttribute("errormsg","帳號不存在"); return "index"; }catch (LockedAccountException e){ model.addAttribute("errormsg","帳號凍結"); return "index"; }catch (IncorrectCredentialsException e){ model.addAttribute("errormsg","帳號密碼錯誤"); return "index"; }catch (AuthenticationException e){ model.addAttribute("errormsg","帳號不存在"); return "index"; } return "success"; } @RequestMapping("/noperms") public String noperms(){ return "noperms"; } @RequestMapping("/admin/test") public @ResponseBody String adminTest (){ return "這是admin的test"; } @RequestMapping("/user/test") public @ResponseBody String UserTest(){ return "這是user的test"; } @RequestMapping("/salt") public @ResponseBody String getrandomSalt(HttpSession httpSession){ String random = UUID.randomUUID().toString(); String randomsalt = DigestUtils.md5DigestAsHex(random.getBytes());//spring 框架下的一個加密工具 //將隨機生辰成的鹽加入到session當中 httpSession.setAttribute("randonsalt",randomsalt); return randomsalt; } }
index.html數據庫
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> <script th:src="@{/js/jquery-3.4.1.min.js}" type="text/javascript"></script> <script th:src="@{/js/JQuery.md5.js}" type="text/javascript"></script> </head> <body> <form action="/login" method="post" id="loginsubmit"> 帳號<input type="text" name="username"> 密碼<input type="text" id="password"> <input type="text" name="password" id="md5password"> <input type="button" value="登陸" id="log"> </form> <span style="color: red">[[${errormsg}]]</span> <script> $(function () { $("#log").bind("click",function () { $.get("/salt","",function (data) { var V_password = $("#password").val(); //用MD5 對密碼加密一次 V_password = $.md5(V_password); //用隨機鹽對 MD5 加密後的密碼再加密一次 $("#md5password").val($.md5(data + V_password)) $("#loginsubmit").submit()} , "text") }) }) </script> </body> </html>
noporms.htmlapache
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>對不起 你沒有操做權限</h1> </body> </html>
success.html瀏覽器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸成功!</title> </head> <body> <h1>登陸成功</h1> <!--logout用於退出當前用戶的登陸的狀態 不用定義在controller中定義在過濾器當中--> <a href="/logout">安全退出</a> <a href="/admin/test">Admintest</a> <a href="/user/test">UserTest</a> </body> </html>