shiro能夠說是簡單卻又功能強大的框架了,比起springSecurity 更加輕巧易用,在實際開發中用到也多,本人所在的公司也是用這個來作認證受權的,下面就介紹一下它的用法html
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> </dependencies>
spring.datasource.username=root spring.datasource.password=1234 spring.datasource.url=jdbc:mysql://localhost:3306/cloud2?useUnicode=true&characterEncoding=UTF8&useServerPrepStmts=true&prepStmtCacheSqlLimit=256&cachePrepStmts=true&prepStmtCacheSize=256&rewriteBatchedStatements=true mybatis.config-location=classpath:mybatis-config.xml mybatis.mapper-locations=classpath*:com/example/demo/**/dao/xml/*.xml logging.level.com.example.demo=DEBUG spring.thymeleaf.cache=false
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true" /> <setting name="lazyLoadingEnabled" value="true" /> <setting name="mapUnderscoreToCamelCase" value="true" /> <setting name="jdbcTypeForNull" value="NULL" /> <setting name="useActualParamName" value="false" /> </settings> </configuration>
@Configuration public class MvcConfig implements WebMvcConfigurer{ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index").setViewName("index"); registry.addViewController("/welcome").setViewName("welcome"); registry.addViewController("/user").setViewName("user"); registry.addViewController("/admin").setViewName("admin"); } }
public class ShiroFilterMapFactory { public static Map<String, String> shiroFilterMap() { // 設置路徑映射,注意這裏要用LinkedHashMap 保證有序 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/welcome", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/user", "roles[user]"); filterChainDefinitionMap.put("/admin", "roles[admin]"); filterChainDefinitionMap.put("/**", "user");//user容許 記住我和受權用戶 訪問,但在進行下單和支付時建議使用authc return filterChainDefinitionMap; } }
import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.MemoryConstrainedCacheManager; import org.apache.shiro.mgt.RememberMeManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.Cookie; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.demo.shiro.realm.ShiroRealm; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; @Configuration public class ShiroConfig { // 這是shiro的大管家,至關於mybatis裏的SqlSessionFactoryBean @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap()); shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; } // web應用管理配置 @Bean public DefaultWebSecurityManager securityManager(Realm shiroRealm,CacheManager cacheManager,RememberMeManager manager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setCacheManager(cacheManager); securityManager.setRememberMeManager(manager);//記住Cookie securityManager.setRealm(shiroRealm); return securityManager; } // 加密算法 @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("MD5");//採用MD5 進行加密 hashedCredentialsMatcher.setHashIterations(1);//加密次數 return hashedCredentialsMatcher; } // 記住個人配置 @Bean public RememberMeManager rememberMeManager() { Cookie cookie = new SimpleCookie("rememberMe"); cookie.setHttpOnly(true);//經過js腳本將沒法讀取到cookie信息 cookie.setMaxAge(60 * 60 * 24);//cookie保存一天 CookieRememberMeManager manager=new CookieRememberMeManager(); manager.setCookie(cookie); return manager; } // 緩存配置 @Bean public CacheManager cacheManager() { MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用內存緩存 return cacheManager; } // 配置realm,用於認證和受權 @Bean public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) { ShiroRealm shiroRealm = new ShiroRealm(); // 校驗密碼用到的算法 shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher); return shiroRealm; } // 啓用shiro方言,這樣能在頁面上使用shiro標籤 @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } /** * 啓用shiro註解 * */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } }
import java.util.HashSet; import java.util.Set; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.SimpleHash; 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 com.example.demo.user.bean.User; import com.example.demo.user.dao.UserDao; //繼承AuthorizingRealm,重寫認證和受權方法 public class ShiroRealm extends AuthorizingRealm { @Autowired private UserDao userDao; /** * 受權方法,若是不設置緩存管理的話,須要訪問須要必定的權限或角色的請求時會進入這個方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User principal = (User) principals.getPrimaryPrincipal(); Set<String> roles = new HashSet<>(); roles.add("user"); if("admin".equals(principal.getUsername())){ roles.add("admin"); } return new SimpleAuthorizationInfo(roles); } /** * 認證方法 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken userToken=(UsernamePasswordToken) token; //根據登陸名查詢用戶,這裏不用校驗密碼,由於密碼的校驗是交給shiro來完成的 User userInfo=userDao.findByUserName(userToken.getUsername()); if(userInfo == null) { throw new IncorrectCredentialsException("用戶名或密碼不正確"); } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( userInfo,//用戶 userInfo.getPassword(),//密碼 ByteSource.Util.bytes(userInfo.getUsername()),//鹽值用 ByteSource.Util.bytes 來生成 getName()//realm name ); return authenticationInfo; } public static void main(String[] args) { //算出鹽值 String credentials="123"; String salt="小蘇"; String hashAlgorithmName="MD5"; int hashIterations=1024; SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); System.out.println(simpleHash); } }
import javax.servlet.http.HttpServletRequest; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.ExcessiveAttemptsException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import com.example.demo.user.bean.User; @Controller public class LoginController { private static Logger logger=LoggerFactory.getLogger(LoginController.class); @PostMapping("login") public ModelAndView login(User user,RedirectAttributes redirectAttributes,boolean rememberMe) { ModelAndView view =new ModelAndView(); String userName = user.getUsername(); Subject currentUser = SecurityUtils.getSubject(); if(!currentUser.isAuthenticated()) { UsernamePasswordToken token =new UsernamePasswordToken(userName,user.getPassword()); try { if(rememberMe) { token.setRememberMe(true); } currentUser.login(token); view.setViewName("redirect:/"); }catch (UnknownAccountException uae) { logger.info("對用戶[" + userName + "]進行登陸驗證..驗證未經過,未知帳戶"); redirectAttributes.addFlashAttribute("message", "未知帳戶"); } catch (IncorrectCredentialsException ice) { logger.info("對用戶[" + userName + "]進行登陸驗證..驗證未經過,錯誤的憑證"); redirectAttributes.addFlashAttribute("message", "用戶名或密碼不正確"); } catch (LockedAccountException lae) { logger.info("對用戶[" + userName + "]進行登陸驗證..驗證未經過,帳戶已鎖定"); redirectAttributes.addFlashAttribute("message", "帳戶已鎖定"); } catch (ExcessiveAttemptsException eae) { logger.info("對用戶[" + userName + "]進行登陸驗證..驗證未經過,錯誤次數過多"); redirectAttributes.addFlashAttribute("message", "用戶名或密碼錯誤次數過多"); } catch (AuthenticationException ae) { //經過處理Shiro的運行時AuthenticationException就能夠控制用戶登陸失敗或密碼錯誤時的情景 logger.info("對用戶[" + userName + "]進行登陸驗證..驗證未經過,堆棧軌跡以下"); ae.printStackTrace(); redirectAttributes.addFlashAttribute("message", "用戶名或密碼不正確"); } } view.setViewName("redirect:/login"); return view; } @GetMapping("/login") public String login(HttpServletRequest request) { try { if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) { return "redirect:/"; } else { logger.info("--進行登陸驗證..驗證開始"); return "login"; } } catch (Exception e) { e.printStackTrace(); } return "login"; } @RequestMapping("su") public ModelAndView su() { ModelAndView view =new ModelAndView("index"); System.out.println("來到了su方法。。。"); return view; } @RequiresRoles({"admin"}) @RequestMapping("xiao") public ModelAndView xiao() { ModelAndView view =new ModelAndView("index"); System.out.println("來到了xiao方法。。。"); return view; } }
import java.io.Serializable; public class User implements Serializable{ private String username; private String password; private String salt; // get/set }
import org.apache.ibatis.annotations.Mapper; import com.example.demo.user.bean.User; @Mapper public interface UserDao { User findByUserName(String username); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo.user.dao.UserDao"> <select id="findByUserName" resultType="com.example.demo.user.bean.User"> select * from user where username=#{username} </select> </mapper>
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; //開啓AspectJAutoProxy @EnableAspectJAutoProxy @SpringBootApplication public class SpringbootShiroApplication { public static void main(String[] args) { SpringApplication.run(SpringbootShiroApplication.class, args); } }
<html> <head> <meta charset="UTF-8"/> <title>login</title> </head> <body> <form action="/login" method="post"> 用戶名:<input type="text" name="username" /><br/> 密碼:<input type="password" name="password" /><br/> <label> <input type="checkbox" name="rememberMe" />記住我 </label><br/> <input type="submit" value="submit" /> </form> </body> </html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"/> <title>login</title> </head> <body> <shiro:guest> <a th:href="@{/login}">登陸</a> <a>註冊</a> </shiro:guest> <shiro:user> 歡迎<shiro:principal property="username"/> <a th:href="@{/logout}">退出</a> </shiro:user> index page.. <shiro:hasRole="user"> 有用戶角色哦、、、、 </shiro:hasRole> <shiro:hasRole="admin"> 有admin角色哦、、、、 </shiro:hasRole> <br> <a th:href="@{/su}">蘇方法</a> <br> <a th:href="@{/xiao}">小方法</a> <br/> <div shiro:hasRole="user"> ===有用戶角色哦、、、、 </div> <br/> <shiro:authenticated> w jinguo renzl... </shiro:authenticated> <br/> <div shiro:hasRole="admin"> ===有admin角色哦、、、、 </div> <div shiro:lacksRole="admin"> ===mei有admin角色哦、、、、 </div> <br/> <script th:src="@{/js/jquery-2.2.3.min.js}"></script> <script> $(function(){ console.log('<shiro:principal property="username"/>'); }) </script> </body> </html>
以上爲shiro標籤的使用,比較簡單,至於shiro有那些能使用的標籤,能夠參考,Shiro-Dialect.xml java
<html> <head> <meta charset="UTF-8"/> <title>login</title> </head> <body> user page.. </body> </html>
admin.html和welcome.html 與其類似,這裏就不寫了mysql
最後便能進行登陸測試,很簡單吧,快來試試吧,體驗shiro的強大功能jquery
1,由於UsernamePasswordToken 保存着表單信息,全部咱們能夠在其getPassword 方法打一個斷點git
2,進行登陸,走到了這個斷點,往前面觀察,就是在這裏進行密碼的比對github
3,在return equals 所在行打一個斷點,其中表單登陸密碼通過加密後獲得結果保存在tokenHashedCredentials中。web
4,accountCredentials 爲咱們認證時放入的從數據庫讀取的密碼算法
5,而後進行比對spring