本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂。

目前我的需求是一个博客系统,有用户和管理员两种角色。一个用户可能有多个角色,每个角色可能有多个权限,每个角色关联不同的菜单(也可以权限和菜单关联)。

本文主要介绍 Shiro 的使用,这里只介绍用户和角色,不需要权限也行。

一、数据库设计

三张表:user、role、user_role

  1. -- ----------------------------
  2. --  Table structure for `role`
  3. -- ----------------------------
  4. DROP TABLE IF EXISTS `role`;
  5. CREATE TABLE `role` (
  6. `id` int(11) NOT NULL AUTO_INCREMENT,
  7. `description` varchar(255) DEFAULT NULL,
  8. `role` varchar(255) DEFAULT NULL,
  9. PRIMARY KEY (`id`)
  10. ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
  11. -- ----------------------------
  12. --  Table structure for `user`
  13. -- ----------------------------
  14. DROP TABLE IF EXISTS `user`;
  15. CREATE TABLE `user` (
  16. `id` int(10) NOT NULL AUTO_INCREMENT,
  17. `password` varchar(100) NOT NULL,
  18. `username` varchar(20) NOT NULL COMMENT '用于登录的账号',
  19. `display_name` varchar(20) DEFAULT NULL COMMENT '显示的用户名',
  20. `email` varchar(100) DEFAULT NULL COMMENT '电子邮箱',
  21. `url` varchar(255) DEFAULT NULL COMMENT '个人主页',
  22. `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  23. `profile` varchar(255) DEFAULT NULL COMMENT '简介',
  24. `create_time` datetime DEFAULT NULL,
  25. `last_login_time` datetime DEFAULT NULL,
  26. `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '正常1,禁止登录0, 已删除-1',
  27. PRIMARY KEY (`id`),
  28. UNIQUE KEY `uq_user_username` (`username`) USING BTREE,
  29. UNIQUE KEY `uq_user_displayname` (`display_name`) USING BTREE,
  30. UNIQUE KEY `uq_user_email` (`email`) USING BTREE
  31. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
  32. -- ----------------------------
  33. --  Table structure for `user_role`
  34. -- ----------------------------
  35. DROP TABLE IF EXISTS `user_role`;
  36. CREATE TABLE `user_role` (
  37. `role_id` int(11) NOT NULL,
  38. `user_id` int(11) NOT NULL,
  39. KEY `role_id` (`role_id`),
  40. KEY `user_id` (`user_id`)
  41. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  42. SET FOREIGN_KEY_CHECKS = 1;

二、SpringBoot 整合 Shiro

1、添加 shiro 依赖

  1. <!--Shiro-->
  2. <dependency>
  3. <groupId>org.apache.shiro</groupId>
  4. <artifactId>shiro-spring</artifactId>
  5. <version>1.4.0</version>
  6. </dependency>

2、MyShiroRealm.java

  1. package com.liuyanzhao.blog.web.config;
  2. import com.liuyanzhao.blog.api.model.Role;
  3. import com.liuyanzhao.blog.api.model.User;
  4. import com.liuyanzhao.blog.api.service.RoleService;
  5. import com.liuyanzhao.blog.api.service.UserService;
  6. import com.liuyanzhao.blog.api.util.Response;
  7. import com.liuyanzhao.blog.web.enums.UserStatus;
  8. import org.apache.shiro.authc.*;
  9. import org.apache.shiro.authz.AuthorizationInfo;
  10. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  11. import org.apache.shiro.realm.AuthorizingRealm;
  12. import org.apache.shiro.subject.PrincipalCollection;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import java.util.List;
  15. /**
  16. * @author 言曌
  17. * @date 2018/9/1 上午10:47
  18. */
  19. public class MyShiroRealm extends AuthorizingRealm {
  20. @Autowired
  21. private UserService userService;
  22. @Autowired
  23. private RoleService roleService;
  24. public static final String SALT = "com.liuyanzhao";
  25. @Override
  26. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  27. System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
  28. SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  29. User user = (User) principals.getPrimaryPrincipal();
  30. List<Role> roleList = roleService.listRolesByUser(user);
  31. for (Role role : roleList) {
  32. authorizationInfo.addRole(role.getRole());
  33. }
  34. return authorizationInfo;
  35. }
  36. @Override
  37. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
  38. throws AuthenticationException {
  39. System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
  40. //获取用户的输入的账号.
  41. String username = (String) token.getPrincipal();
  42. System.out.println(token.getCredentials());
  43. //通过username从数据库中查找 User对象,如果找到,没找到.
  44. //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
  45. Response<User> response = userService.getUserByUsername(username);
  46. if (!response.getSuccess()) {
  47. return null;
  48. }
  49. User user = response.getData();
  50. if (UserStatus.LOCKED.getCode().equals(user.getStatus())) {
  51. throw new LockedAccountException(username + "账号被锁定,请联系管理员!");
  52. }
  53. SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
  54. user,
  55. user.getPassword(),
  56. getName()
  57. );
  58. return authenticationInfo;
  59. }
  60. }

这个是自定义验证账号密码和验证是否有权限。

其中 UserService 和 RoleService 这里就不用给了,一个是根据用户名获得用户,一个是根据获得用户的权限列表。

3、ShiroConfig.java

  1. package com.liuyanzhao.blog.web.config;
  2. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
  3. import org.apache.shiro.mgt.SecurityManager;
  4. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
  5. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  6. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
  10. import java.util.LinkedHashMap;
  11. import java.util.Map;
  12. import java.util.Properties;
  13. /**
  14. * @author 言曌
  15. * @date 2018/8/20 上午6:19
  16. */
  17. @Configuration
  18. public class ShiroConfig {
  19. @Bean
  20. public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
  21. System.out.println("ShiroConfiguration.shirFilter()");
  22. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  23. shiroFilterFactoryBean.setSecurityManager(securityManager);
  24. //拦截器.
  25. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
  26. // 配置不会被拦截的链接 顺序判断
  27. filterChainDefinitionMap.put("/css/**", "anon");
  28. filterChainDefinitionMap.put("/js/**", "anon");
  29. filterChainDefinitionMap.put("/img/**", "anon");
  30. filterChainDefinitionMap.put("/components/**", "anon");
  31. filterChainDefinitionMap.put("/favicon.ico", "anon");
  32. //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
  33. filterChainDefinitionMap.put("/logout", "logout");
  34. //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
  35. //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
  36. filterChainDefinitionMap.put("/admin/**", "authc");
  37. filterChainDefinitionMap.put("/user/**", "authc");
  38. filterChainDefinitionMap.put("/**", "anon");
  39. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  40. // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
  41. shiroFilterFactoryBean.setLoginUrl("/login");
  42. // 登录成功后要跳转的链接
  43. shiroFilterFactoryBean.setSuccessUrl("/");
  44. //未授权界面;
  45. shiroFilterFactoryBean.setUnauthorizedUrl("/403");
  46. return shiroFilterFactoryBean;
  47. }
  48. /**
  49. * 凭证匹配器
  50. * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
  51. * )
  52. * @return
  53. */
  54. @Bean
  55. public HashedCredentialsMatcher hashedCredentialsMatcher(){
  56. HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
  57. //散列算法:这里使用MD5算法;
  58. hashedCredentialsMatcher.setHashAlgorithmName("md5");
  59. //散列的次数,比如散列两次,相当于 md5(md5(""));
  60. hashedCredentialsMatcher.setHashIterations(2);
  61. return hashedCredentialsMatcher;
  62. }
  63. @Bean
  64. public MyShiroRealm myShiroRealm(){
  65. MyShiroRealm myShiroRealm = new MyShiroRealm();
  66. myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
  67. return myShiroRealm;
  68. }
  69. @Bean
  70. public SecurityManager securityManager(){
  71. DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
  72. //设置realm
  73. securityManager.setRealm(myShiroRealm());
  74. return securityManager;
  75. }
  76. /**
  77. *  开启shiro aop注解支持.
  78. *  使用代理方式;所以需要开启代码支持;
  79. * @param securityManager
  80. * @return
  81. */
  82. @Bean
  83. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
  84. AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
  85. authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
  86. return authorizationAttributeSourceAdvisor;
  87. }
  88. @Bean(name="simpleMappingExceptionResolver")
  89. public SimpleMappingExceptionResolver
  90. createSimpleMappingExceptionResolver() {
  91. SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
  92. Properties mappings = new Properties();
  93. //数据库异常处理
  94. mappings.setProperty("DatabaseException", "databaseError");
  95. mappings.setProperty("UnauthorizedException","403");
  96. r.setExceptionMappings(mappings);
  97. r.setDefaultErrorView("error");
  98. r.setExceptionAttribute("message");
  99. return r;
  100. }
  101. }

这里补充一下链接器链,是按顺序匹配的。必须使用LinkedHashMap,因为HashMap遍历是无序的。

目前我是放行除 /admin/** 和 /user/** 之外所有的页面,通常情况下是放行匿名的页面,其他的一律需要授权验证,如

filterChainDefinitionMap.put("/**", "authc");

4、LoginParam.java

  1. package com.liuyanzhao.blog.web.param;
  2. import lombok.Data;
  3. import java.io.Serializable;
  4. /**
  5. * 登录参数
  6. * @author 言曌
  7. * @date 2018/9/9 上午11:42
  8. */
  9. @Data
  10. public class LoginParam implements Serializable{
  11. private static final long serialVersionUID = 166457193110647497L;
  12. private String username;
  13. private String password;
  14. private String captchaCode;
  15. private boolean rememberMe;
  16. private String CSRFToken;
  17. }

5、LoginController.java

  1. /**
  2. * 登录页面
  3. *
  4. * @return
  5. */
  6. @GetMapping("/login")
  7. public String loginPage() {
  8. return "login";
  9. }
  10. /**
  11. * 登录提交
  12. *
  13. * @param loginParam
  14. * @param model
  15. * @return
  16. * @throws Exception
  17. */
  18. @PostMapping("/login")
  19. public String login(LoginParam loginParam,
  20. Model model) throws Exception {
  21. //1、验证用户名和密码
  22. org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject();
  23. UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginParam.getUsername(), loginParam.getPassword(), loginParam.isRememberMe());
  24. String msg = "";
  25. try {
  26. subject.login(usernamePasswordToken);
  27. return "redirect:/";
  28. } catch (UnknownAccountException e) {
  29. log.info("UnknownAccountException -- > 账号不存在:");
  30. msg = "账号不存在!";
  31. } catch (IncorrectCredentialsException e) {
  32. log.info("IncorrectCredentialsException -- > 密码不正确:");
  33. msg = "密码不正确!";
  34. } catch (LockedAccountException e) {
  35. log.info("LockedAccountException -- > 账号被锁定");
  36. msg = "账号被锁定!";
  37. } catch (Exception e) {
  38. log.info(e.getMessage());
  39. }
  40. model.addAttribute("msg", msg);
  41. return "login";
  42. }

6、login.html

  1. <form name="loginform" id="loginform" action="/login" method="post">
  2. <p>
  3. <label for="username">用户名或电子邮件地址<br/>
  4. <input type="text" name="username" id="username" class="input" size="20" required/>
  5. </label>
  6. </p>
  7. <p>
  8. <label for="password">密码<br/>
  9. <input type="password" name="password" id="password" class="input" size="20" required/>
  10. </label>
  11. </p>
  12. <p th:if="${msg}">
  13. <label for="captchaCode">验证码<br/>
  14. <input type="text" name="captchaCode" id="captchaCode" class="input" size="20"
  15. style="float:left;width: 40%; " required/>
  16. <img src="/img/getKaptchaImage" alt="" style="float:left;padding-top: 3px;">
  17. <span>换一张</span>
  18. </label>
  19. </p>
  20. <input type="hidden" name="CSRFToken" th:value="${session.CSRFToken}">
  21. <div style="clear: both;"></div>
  22. <p class="forgetmenot">
  23. <label for="rememberme">
  24. <input name="rememberMe" type="checkbox" id="rememberMe"
  25. checked="checked"> 记住我的登录信息</label>
  26. </p>
  27. <p class="submit">
  28. <input type="submit" class="button button-primary button-large" value="登录"/>
  29. </p>
  30. <br>
  31. </form>

这里主要关注 form 表单提交的 username 和 password 即可,其中 CSRF 防护忽略,rememberMe 下面要用到。

三、添加 kaptcha 验证码

1、添加验证码依赖

  1. <!--验证码-->
  2. <dependency>
  3. <groupId>com.github.penggle</groupId>
  4. <artifactId>kaptcha</artifactId>
  5. <version>2.3.2</version>
  6. </dependency>

2、验证码配置类

  1. package com.liuyanzhao.blog.web.config;
  2. import com.google.code.kaptcha.impl.DefaultKaptcha;
  3. import com.google.code.kaptcha.util.Config;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import java.util.Properties;
  7. /**
  8. * 验证码图片样式配置
  9. * @author 言曌
  10. * @date 2018/9/2 上午10:23
  11. */
  12. @Configuration
  13. public class KaptchaConfig {
  14. @Bean(name="captchaProducer")
  15. public DefaultKaptcha getKaptchaBean(){
  16. DefaultKaptcha defaultKaptcha=new DefaultKaptcha();
  17. Properties properties=new Properties();
  18. //验证码字符范围
  19. properties.setProperty("kaptcha.textproducer.char.string", "23456789");
  20. //图片边框颜色
  21. properties.setProperty("kaptcha.border.color", "245,248,249");
  22. //字体颜色
  23. properties.setProperty("kaptcha.textproducer.font.color", "black");
  24. //文字间隔
  25. properties.setProperty("kaptcha.textproducer.char.space", "1");
  26. //图片宽度
  27. properties.setProperty("kaptcha.image.width", "100");
  28. //图片高度
  29. properties.setProperty("kaptcha.image.height", "35");
  30. //字体大小
  31. properties.setProperty("kaptcha.textproducer.font.size", "30");
  32. //session的key
  33. //        properties.setProperty("kaptcha.session.key", "code");
  34. //长度
  35. properties.setProperty("kaptcha.textproducer.char.length", "4");
  36. //字体
  37. properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
  38. Config config=new Config(properties);
  39. defaultKaptcha.setConfig(config);
  40. return defaultKaptcha;
  41. }
  42. }

3、验证码控制器

  1. package com.liuyanzhao.blog.web.controller.common;
  2. import com.google.code.kaptcha.Constants;
  3. import com.google.code.kaptcha.Producer;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.web.bind.annotation.GetMapping;
  8. import javax.imageio.ImageIO;
  9. import javax.servlet.ServletOutputStream;
  10. import java.awt.image.BufferedImage;
  11. /**
  12. * 验证码控制器
  13. * @author 言曌
  14. * @date 2018/9/2 上午10:41
  15. */
  16. @Controller
  17. @Slf4j
  18. public class KaptchaController extends BaseController {
  19. @Autowired
  20. private Producer captchaProducer;
  21. @GetMapping("/img/getKaptchaImage")
  22. public void getKaptchaImage() throws Exception {
  23. response.setDateHeader("Expires", 0);
  24. // Set standard HTTP/1.1 no-cache headers.
  25. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
  26. // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
  27. response.addHeader("Cache-Control", "post-check=0, pre-check=0");
  28. // Set standard HTTP/1.0 no-cache header.
  29. response.setHeader("Pragma", "no-cache");
  30. // return a jpeg
  31. response.setContentType("image/jpeg");
  32. // create the text for the image
  33. String capText = captchaProducer.createText();
  34. //将验证码存到session
  35. session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
  36. log.info(capText);
  37. // create the image with the text
  38. BufferedImage bi = captchaProducer.createImage(capText);
  39. ServletOutputStream out = response.getOutputStream();
  40. // write the data out
  41. ImageIO.write(bi, "jpg", out);
  42. try {
  43. out.flush();
  44. } finally {
  45. out.close();
  46. }
  47. }
  48. }

4、修改 LoginController.java

  1. /**
  2. * 登录提交
  3. *
  4. * @param loginParam
  5. * @param model
  6. * @return
  7. * @throws Exception
  8. */
  9. @PostMapping("/login")
  10. public String login(LoginParam loginParam,
  11. Model model) throws Exception {
  12. //1、检验验证码
  13. if (loginParam.getCaptchaCode() != null) {
  14. String inputCode = request.getParameter("captchaCode");
  15. String captchaSession = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
  16. if (!Objects.equals(inputCode, captchaSession)) {
  17. log.info("验证码错误,用户输入:{}, 正确验证码:{}", inputCode, captchaSession);
  18. model.addAttribute("msg", "验证码不正确!");
  19. CsrfTokenUtil.refreshToken(request);
  20. return "login";
  21. }
  22. }
  23. //2、验证用户名和密码
  24. org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject();
  25. UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginParam.getUsername(), loginParam.getPassword());
  26. String msg = "";
  27. try {
  28. subject.login(usernamePasswordToken);
  29. return "redirect:/";
  30. } catch (UnknownAccountException e) {
  31. log.info("UnknownAccountException -- > 账号不存在:");
  32. msg = "账号不存在!";
  33. } catch (IncorrectCredentialsException e) {
  34. log.info("IncorrectCredentialsException -- > 密码不正确:");
  35. msg = "密码不正确!";
  36. } catch (LockedAccountException e) {
  37. log.info("LockedAccountException -- > 账号被锁定");
  38. msg = "账号被锁定!";
  39. } catch (Exception e) {
  40. log.info(e.getMessage());
  41. }
  42. model.addAttribute("msg", msg);
  43. CsrfTokenUtil.refreshToken(request);
  44. return "login";
  45. }

四、配置记住我

1、修改 ShiroConfig.java

  1. /**
  2. * cookie对象;
  3. * rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。
  4. * @return
  5. */
  6. @Bean
  7. public SimpleCookie rememberMeCookie(){
  8. //System.out.println("ShiroConfiguration.rememberMeCookie()");
  9. //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
  10. SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
  11. //<!-- 记住我cookie生效时间30天 ,单位秒;-->
  12. simpleCookie.setMaxAge(259200);
  13. return simpleCookie;
  14. }
  15. /**
  16. * cookie管理对象;
  17. * rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
  18. * @return
  19. */
  20. @Bean
  21. public CookieRememberMeManager rememberMeManager(){
  22. //System.out.println("ShiroConfiguration.rememberMeManager()");
  23. CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
  24. cookieRememberMeManager.setCookie(rememberMeCookie());
  25. //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
  26. cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
  27. return cookieRememberMeManager;
  28. }
  29. @Bean
  30. public SecurityManager securityManager(){
  31. DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
  32. //设置realm
  33. securityManager.setRealm(myShiroRealm());
  34. //用户授权/认证信息Cache, 采用EhC//注入记住我管理器
  35. securityManager.setRememberMeManager(rememberMeManager());
  36. return securityManager;
  37. }

2、修改 LoginController.java

主要是修改

  1. UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginParam.getUsername(), loginParam.getPassword(), loginParam.isRememberMe());

最终如下

  1. /**
  2. * 登录提交
  3. *
  4. * @param loginParam
  5. * @param model
  6. * @return
  7. * @throws Exception
  8. */
  9. @PostMapping("/login")
  10. public String login(LoginParam loginParam,
  11. Model model) throws Exception {
  12. //1、检验验证码
  13. if (loginParam.getCaptchaCode() != null) {
  14. String inputCode = request.getParameter("captchaCode");
  15. String captchaSession = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
  16. if (!Objects.equals(inputCode, captchaSession)) {
  17. log.info("验证码错误,用户输入:{}, 正确验证码:{}", inputCode, captchaSession);
  18. model.addAttribute("msg", "验证码不正确!");
  19. CsrfTokenUtil.refreshToken(request);
  20. return "login";
  21. }
  22. }
  23. //2、验证用户名和密码
  24. org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject();
  25. UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginParam.getUsername(), loginParam.getPassword(), loginParam.isRememberMe());
  26. String msg = "";
  27. try {
  28. subject.login(usernamePasswordToken);
  29. return "redirect:/";
  30. } catch (UnknownAccountException e) {
  31. log.info("UnknownAccountException -- > 账号不存在:");
  32. msg = "账号不存在!";
  33. } catch (IncorrectCredentialsException e) {
  34. log.info("IncorrectCredentialsException -- > 密码不正确:");
  35. msg = "密码不正确!";
  36. } catch (LockedAccountException e) {
  37. log.info("LockedAccountException -- > 账号被锁定");
  38. msg = "账号被锁定!";
  39. } catch (Exception e) {
  40. log.info(e.getMessage());
  41. }
  42. model.addAttribute("msg", msg);
  43. CsrfTokenUtil.refreshToken(request);
  44. return "login";
  45. }

3、login.html 添加记住我的复选框

name为之前填的 rememberMe

4、修改 ShiroConfig.java

上面的配置后,当登录后,会创建rememberMe的 cookie,退出浏览器,cookie依然存在。

但是我们访问需要登录(authc)的页面会被拦截到登录页面

解决办法是修改 authc 为 user

我们要修改

  1. filterChainDefinitionMap.put("/admin/**", "authc");
  2. filterChainDefinitionMap.put("/user/**", "authc");

  1. filterChainDefinitionMap.put("/admin/**", "user");
  2. filterChainDefinitionMap.put("/user/**", "user");

然后再次尝试,发现可以访问。

五、效果图如下

首次登录无需验证码,登录错误需要验证码

SpringBoot整合Shiro实现权限控制,验证码的更多相关文章

  1. SpringBoot整合Shiro实现权限控制

    目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...

  2. springboot集成shiro 实现权限控制(转)

    shiro apache shiro 是一个轻量级的身份验证与授权框架,与spring security 相比较,简单易用,灵活性高,springboot本身是提供了对security的支持,毕竟是自 ...

  3. SpringBoot集成Shiro实现权限控制

    Shiro简介 Apache Shiro是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理.使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移 ...

  4. springboot整合shiro进行权限管理

    背景:springboot2.1,shiro1.4:由于目前的小项目没做登录,但是客户又需要加上权限,因此楼主就想到了shiro(这是单独的项目,需要集成后台管理系统) shiro简介 Apache ...

  5. spring-boot整合shiro作权限认证

    spring-shiro属于轻量级权限框架,即使spring-security更新换代,市场上大多数企业还是选择shiro 废话不多说  引入pom文件 <!--shiro集成spring--& ...

  6. shiro系列三、ssm框架整合shiro实现权限控制

    shiro权限框架是一个非常优秀的框架,前面的几篇文章对shiro进行了非常详细的介绍和原理分析,那么接下来让我们开始在web项目中使用它(javase也能用shiro): 一.数据库表结构设计 二. ...

  7. spring-boot整合shiro实现权限管理

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...

  8. SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)

    导入依赖(pom.xml)  <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...

  9. SpringBoot整合Shiro权限框架实战

    什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...

随机推荐

  1. HDU 1789 Doing Homework again(排序,DP)

    Doing Homework again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  2. C++ bool、三目运算符、引用

    bool变量: C++相对于C语言加入了bool变量,其值为true(1) 和 false(0).true表示不为零的数  false表示为零的数,占用一个字节的空间. 代码: /* 编译环境  gc ...

  3. qt tableview里面添加控件

    在QStyledItemDelegate的paint方法里面 void MyItemModelDeletage::paint(QPainter *painter, const QStyleOption ...

  4. Hack the box: Bastion

    介绍 目标:10.10.10.134 (Windows) Kali:10.10.16.65 In conclusion, Bastion is not a medium box. But it wou ...

  5. vue.js(2)--v-cloak v-text v-html

    v-cloak v-text  v-html的使用 (1)实例 <!DOCTYPE html> <html lang="en"> <head> ...

  6. java线程中的同步锁和互斥锁有什么区别?

    两者都包括对资源的独占. 区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,也没有固定的执行顺序. 2:同步是线程通过一定的逻辑顺序占有资源,有一定的合作关系去完成任务.

  7. JS异常missing ) after argument list

    JS异常报错 missing ) after argument list 在使用JS拼接DOM元素时,有这种情况发生,'<a onclick="del(' + data.id + ') ...

  8. dataTable 、dataView、Dataset 区别

    dataTable .dataView.Dataset 区别的经典回答 1.DataView是DataTable的表示,DataTable表里的记录是没有顺序的,但显示可以有不同顺序(DataVIew ...

  9. valgrind 性能测试工具学习使用

    一.valgrind简介 Valgrind工具套件提供了许多调试和分析工具,可帮助您使程序更快,更正确.这些工具中最受欢迎的是Memcheck.它可以检测许多与C和C ++程序中常见的内存相关的错误, ...

  10. DP+滚动数组 || [Usaco2007 Nov]Telephone Wire 架设电话线 || BZOJ 1705 || Luogu P2885

    本来是懒得写题解的…想想还是要勤发题解和学习笔记…然后就滚过来写题解了. 题面:[USACO07NOV]电话线Telephone Wire 题解: F[ i ][ j ] 表示前 i 根电线杆,第 i ...