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