這裏同時導入了log4j, Shiro默認的日誌是commons-logginghtml
<dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.6.0</version> </dependency> <!-- configure logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <scope>runtime</scope> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <scope>runtime</scope> <version>1.7.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <scope>runtime</scope> <version>1.2.17</version> </dependency> </dependencies>
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n # General Apache libraries log4j.logger.org.apache=WARN # Spring log4j.logger.org.springframework=WARN # Default Shiro logging log4j.logger.org.apache.shiro=INFO # Disable verbose logging log4j.logger.org.apache.shiro.util.ThreadContext=WARN log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # ============================================================================= # Quickstart INI Realm configuration # # For those that might not understand the references in this file, the # definitions are all based on the classic Mel Brooks' film "Spaceballs". ;) # ============================================================================= # ----------------------------------------------------------------------------- # Users and their assigned roles # # Each line conforms to the format defined in the # org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc # ----------------------------------------------------------------------------- [users] # user 'root' with password 'secret' and the 'admin' role root = secret, admin # user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest # user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz # ----------------------------------------------------------------------------- # Roles with assigned permissions # # Each line conforms to the format defined in the # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc # ----------------------------------------------------------------------------- [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(securityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: //獲取當前的用戶對象 Subject Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) //經過當前用戶拿到 session (能夠脫離Web) Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: //判斷當前的用戶是否被認證 if (!currentUser.isAuthenticated()) { //Token : 令牌, 此處沒有獲取, 隨機設置 UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); //設置記住我 token.setRememberMe(true); try { //執行登陸操做 currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? //認證異常(總的異常) catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! //註銷 currentUser.logout(); //結束啓動 System.exit(0); } }
Shiro三大核心前端
<!--shiro整合包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.6.0</version> </dependency>
package com.wang.config; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; //自定義的 UserRealm, 繼承AuthorizingRealm便可 public class UserRealm extends AuthorizingRealm { //受權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("執行了 => AuthorizationInfo 受權"); return null; } //認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("執行了 => AuthenticationInfo 認證"); return null; } }
注意java
package com.wang.config; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; @Configuration public class ShiroConfig { //ShiroFilterFactoryBean : 3 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //關聯SecurityManager, 設置安全管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //添加shiro的內置過濾器 /* anon: 無需認證, 就能夠訪問 authc: 必須認證了才能訪問 user: 必須擁有 記住我 功能才能訪問 perms: 擁有對於某個資源的權限才能訪問 role: 擁有某個角色權限才能訪問 */ LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // filterChainDefinitionMap.put("/user/add", "authc"); // filterChainDefinitionMap.put("/user/update", "authc"); filterChainDefinitionMap.put("/user/*", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); //設置登陸的請求 shiroFilterFactoryBean.setLoginUrl("/toLogin"); return shiroFilterFactoryBean; } //DefaultWebSecurityManager : 2 //@Qualifier() 利用bean的id注入, 在註解託管中即爲方法名 @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //關聯UserRealm securityManager.setRealm(userRealm); return securityManager; } //建立realm對象, 須要自定義 : 1 //將本身定義的Realm註冊爲Bean, 被SpringBoot託管 @Bean public UserRealm userRealm() { return new UserRealm(); } }
注意:git
anon: 無需認證, 就能夠訪問github
authc: 必須認證了才能訪問web
user: 必須擁有 記住我 功能才能訪問spring
perms: 擁有對於某個資源的權限才能訪問數據庫
role: 擁有某個角色權限才能訪問express
package com.wang.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class MyController { @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"; } }
前端頁面代碼略apache
這裏寫在controller中, 利用不一樣的結果輸出提示信息並進行跳轉
@RequestMapping("/login") public String login(String username, String password, Model model) { //獲取當前的用戶 Subject subject = SecurityUtils.getSubject(); //封裝用戶的登陸數據 UsernamePasswordToken token = new UsernamePasswordToken(username, password); //執行登陸的方法, 若是沒有異常, 說明登陸OK try { subject.login(token); return "index"; } catch (UnknownAccountException e) { //用戶名不存在 model.addAttribute("msg", "用戶名錯誤"); return "login"; } catch (IncorrectCredentialsException e) { //密碼不存在 model.addAttribute("msg", "密碼錯誤"); return "login"; }
注意
在自定義的UserRealm中, 進行配置
//認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("執行了 => AuthenticationInfo 認證"); //用戶名, 密碼 ==> 數據庫中取 String name = "root"; String password = "123456"; UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; //用戶名認證 if (!userToken.getUsername().equals(name)) { //拋出異常 UnknownAccountException return null; } //密碼認證 : shiro作 return new SimpleAuthenticationInfo("", password, ""); }
注意
Shiro不須要導入與Mybatis的整合, 只須要導入與thymeleaf的整合包
<!--shiro整合thymeleaf--> <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
package com.wang.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; @Configuration public class ShiroConfig { //ShiroFilterFactoryBean : 3 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //關聯SecurityManager, 設置安全管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //添加shiro的內置過濾器 /* anon: 無需認證, 就能夠訪問 authc: 必須認證了才能訪問 user: 必須擁有 記住我 功能才能訪問 perms: 擁有對於某個資源的權限才能訪問 role: 擁有某個角色權限才能訪問 */ //攔截 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // filterChainDefinitionMap.put("/user/add", "authc"); // filterChainDefinitionMap.put("/user/update", "authc"); //受權 filterChainDefinitionMap.put("/user/add", "perms[user:add]"); filterChainDefinitionMap.put("/user/update", "perms[user:update]"); filterChainDefinitionMap.put("/user/*", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); //設置登陸的請求 shiroFilterFactoryBean.setLoginUrl("/toLogin"); //未受權頁面 shiroFilterFactoryBean.setUnauthorizedUrl("/noauth"); return shiroFilterFactoryBean; } //DefaultWebSecurityManager : 2 //@Qualifier() 利用bean的id注入, 在註解託管中即爲方法名 @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //關聯UserRealm securityManager.setRealm(userRealm); return securityManager; } //建立realm對象, 須要自定義 : 1 //將本身定義的Realm註冊爲Bean, 被SpringBoot託管 @Bean public UserRealm userRealm() { return new UserRealm(); } //整合ShiroDialect: 用來整合 shiro thymeleaf @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } }
注意
package com.wang.config; import com.wang.pojo.User; import com.wang.service.UserServiceImpl; 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.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; //自定義的 UserRealm, 繼承AuthorizingRealm便可 public class UserRealm extends AuthorizingRealm { @Autowired private UserServiceImpl userService; //受權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("執行了 => AuthorizationInfo 受權"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //拿到當前登陸的這個對象, user在下面已經放到了subject中 Subject subject = SecurityUtils.getSubject(); //拿到user對象 User currentUser = (User) subject.getPrincipal(); //放到perms中, 在ShiroConfig中以K-V調用 info.addStringPermission(currentUser.getPerms()); return info; } //認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("執行了 => AuthenticationInfo 認證"); //用戶名, 密碼 ==> 數據庫中取 //從token中得到用戶名 UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; String username = userToken.getUsername(); User user = userService.queryUserByName(username); //輸入的用戶名在數據庫中不存在 if (user == null) { return null; } Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser", user); String password = user.getPwd(); //密碼認證 : shiro作(password與token從前端取出的密碼進行比較) //此處傳入一個user, 放到了Subject中 return new SimpleAuthenticationInfo(user, password, ""); } }
注意
受權
認證
用戶名和密碼已經放在了token中 UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; 這個token是從controller層中利用從前端的數據封裝好的
這裏能夠設置Subject對象的session, 存放一個loginUser字段, 判斷是否有User從而選擇是否在前端顯示登錄的連接, 這裏的session不是Web中的session, 是Shiro自帶的, 全局都能用(和web的session差很少)
SimpleAuthenticationInfo對象一樣的是返回咱們設置完的認證對象, 參數設置以下
principal the 'primary' principal associated with the specified realm. credentials the credentials that verify the given principal. realmName the realm from where the principal and credentials were acquired. public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName)
此處的用戶名, 密碼, 以及用戶對象都從數據庫中取出
此處的放回值 return new SimpleAuthenticationInfo(user, password, ""); 裏面傳遞了咱們從數據庫中取出的user, 在受權時因爲關聯了字段, 所以Shiro會幫咱們從user中取出perms對應的字段, 和config中的過濾器進行比較
@RequestMapping("/login") public String login(String username, String password, Model model) { //獲取當前的用戶 Subject subject = SecurityUtils.getSubject(); //封裝用戶的登陸數據 UsernamePasswordToken token = new UsernamePasswordToken(username, password); //執行登陸的方法, 若是沒有異常, 說明登陸OK try { subject.login(token); return "index"; } catch (UnknownAccountException e) { //用戶名不存在 model.addAttribute("msg", "用戶名錯誤"); return "login"; } catch (IncorrectCredentialsException e) { //密碼不存在 model.addAttribute("msg", "密碼錯誤"); return "login"; } }
注意
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首頁</h1> <p> <span th:text="${msg}"></span> </p> <p th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">登陸</a> </p> <hr> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="user:update"> <a th:href="@{/user/update}">update</a> </div> </body> </html>
注意