栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

Spring Boot 整合 Shiro 实现登录认证与权限控制

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Boot 整合 Shiro 实现登录认证与权限控制

用户角色权限数据库设计

数据库这里以 MySQL 为例

创建数据库

所需表如下:

  • user:用户表
  • role:角色表
  • perm:权限菜单表
  • user_role:用户与角色关联的中间表
  • role_prem:角色与权限菜单关联的中间表
执行数据库脚本


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for perm
-- ----------------------------
DROP TABLE IF EXISTS `perm`;
CREATE TABLE `perm`  (
  `perm_id` int(32) NOT NULL COMMENT '权限主键',
  `perm_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '权限url',
  `perm_description` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '权限描述',
  PRIMARY KEY (`perm_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of perm
-- ----------------------------
INSERT INTO `perm` VALUES (1, '/user
    User selectByUsername(String username);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User selectByUsername(String username) {

 return userMapper.selectByUsername(username);
    }
}
引入依赖

在 pox.xml 中添加 org.apache.shiro:shiro-spring 和 com.github.theborakompanioni:thymeleaf-extras-shiro 依赖


    2.0.0
    1.4.0



    
    
 org.apache.shiro
 shiro-spring
 ${shiro.version}
    

    
    
      com.github.theborakompanioni
      thymeleaf-extras-shiro
      ${thymeleaf-extras-shiro.version}
    

自定义认证和授权

创建 MyRealm 类实现认证与授权

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.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;


public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
 User user = userService.selectByUsername(token.getUsername());
 if (user == null) {
     throw new UnknownAccountException();
 }
 return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }

    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

 Subject subject = SecurityUtils.getSubject();
 User user = (User) subject.getPrincipal();
 if (user != null) {
     SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
     List roles = new linkedList<>();
     List perms = new linkedList<>();
     for (Role role : user.getRoleList()) {
  roles.add(role.getRoleName());
     }
     for (Perm perm : user.getPermList()) {
  perms.add(perm.getPermUrl());
     }
     simpleAuthorizationInfo.addRoles(roles);
     simpleAuthorizationInfo.addStringPermissions(perms);
     return simpleAuthorizationInfo;
 }
 return null;
    }
}
Shiro 配置类

创建 ShiroConfig 配置类

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.linkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

    
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {

 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
 // 散列算法(加密)
 credentialsMatcher.setHashAlgorithmName("MD5");
 // 散列次数(加密次数)
 credentialsMatcher.setHashIterations(1);
 // storedCredentialsHexEncoded 默认是true,此时用的是密码加密用的是Hex编码;false时用base64编码
 credentialsMatcher.setStoredCredentialsHexEncoded(true);
 return credentialsMatcher;
    }

    
    @Bean("MyRealm")
    public MyRealm MyRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {

 MyRealm MyRealm = new MyRealm();
 MyRealm.setCredentialsMatcher(matcher);
 return MyRealm;
    }

    
    @Bean
    public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {

 ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
 bean.setSecurityManager(securityManager);
 bean.setSuccessUrl("/index.html");
 bean.setLoginUrl("/login.html");
 bean.setUnauthorizedUrl("/unauthorized.html");

 
 Map filterMap = new linkedHashMap<>();
 
 filterMap.put("/image
 filterMap.put("/login.html", "anon");
 filterMap.put("/login.do", "anon");
 
 filterMap.put("
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(HashedCredentialsMatcher hashedCredentialsMatcher, @Qualifier("sessionManager") DefaultWebSessionManager defaultWebSessionManager) {

 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 securityManager.setRealm(MyRealm(hashedCredentialsMatcher));
 securityManager.setSessionManager(defaultWebSessionManager);
 return securityManager;
    }

    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {

 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
 return authorizationAttributeSourceAdvisor;
    }
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

 DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
 defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
 return defaultAdvisorAutoProxyCreator;
    }

    
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {

 SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
 Properties properties = new Properties();
 // 未认证跳转页面(跳转路径为项目里的页面相对路径,并非 URL)
 properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "login");
 // 权限不足跳转页面
 properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "unauthorized");
 resolver.setExceptionMappings(properties);
 return resolver;
    }

    
    @Bean("sessionManager")
    public DefaultWebSessionManager defaultWebSessionManager() {

 DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
 // 设置用户登录信息失效时间为一天(单位:ms)
 defaultWebSessionManager.setGlobalSessionTimeout(1000L * 60L * 60L * 24L);
 return defaultWebSessionManager;
    }

    
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
 return new ShiroDialect();
    }
}
Controller
@Controller
public class IndexController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "login.html")
    public String loginView() {

 // 判断当前用户是否通过认证
 if (SecurityUtils.getSubject().isAuthenticated()) {
     // 认证通过,重定向到首页
     return "redirect:index.html";
 } else {
     // 未认证或认证失败,转发到登录页
     return "login";
 }
    }

    @RequestMapping(value = "login.do")
    @ResponseBody
    public AppReturn loginDo(@RequestParam String username, @RequestParam String password) {
 Subject subject = SecurityUtils.getSubject();
 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
 try {
     // 执行认证
     subject.login(usernamePasswordToken);
 } catch (UnknownAccountException e) {
     return AppReturn.defeated("账号不存在");
 } catch (IncorrectCredentialsException e) {
     return AppReturn.defeated("密码错误");
 }
 return AppReturn.succeed("登录成功");
    }

    @RequestMapping(value = "index.html")
    public String indexView() {
 return "index";
    }

    @RequestMapping(value = "logout.do")
    public String logoutDo() {

 if (SecurityUtils.getSubject().isAuthenticated()) {
     // 退出
     SecurityUtils.getSubject().logout();
 }
 return "redirect:login.html";
    }

    @RequestMapping(value = "unauthorized.html")
    public String unauthorizedView() {

 return "unauthorized";
    }
}
@Controller
public class IndexController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "login.html")
    public String loginView() {

 // 判断当前用户是否通过认证
 if (SecurityUtils.getSubject().isAuthenticated()) {
     // 认证通过,重定向到首页
     return "redirect:index.html";
 } else {
     // 未认证或认证失败,转发到登录页
     return "login";
 }
    }

    @RequestMapping(value = "login.do")
    @ResponseBody
    public AppReturn loginDo(@RequestParam String username, @RequestParam String password) {
 return userService.loginDo(username, password);
    }

    @RequestMapping(value = "index.html")
    public String indexView() {
 return "index";
    }

    @RequestMapping(value = "logout.do")
    public String logoutDo() {

 if (SecurityUtils.getSubject().isAuthenticated()) {
     // 退出
     SecurityUtils.getSubject().logout();
 }
 return "redirect:login.html";
    }

    @RequestMapping(value = "unauthorized.html")
    public String unauthorizedView() {

 return "unauthorized";
    }
}
Web 页面

引入 jquery.js

login.html




    
    登录


    
用户名:
密码:
id="tip" class="tip">
index.html




    
    首页


    Hello Shiro
    退出


unauthorized.html




    
    无权访问


    权限不足


Java 中使用 Shiro 权限注解

除了在 ShiroConfig 配置类中自定义权限过滤规则,还可以使用 Shiro 提供的注解实现权限过滤,在 Controller 中的每个请求方法上可以添加以下注解实现权限控制:

@RequiresAuthentication: 只有认证通过的用户才能访问

@RequiresRoles(value = {“root”}, logical = Logical.OR)

  • value:指定拥有 root 角色才能访问,角色可以是多个,以逗号隔开
  • logical:该属性有两个值,Logical.OR(只要拥有其中一个角色就能访问),Logical.AND(需要拥有指定的全部角色才能访问,否则会抛出权限不足异常)

@RequiresPermissions(value = {“/user/delete”}, logical = Logical.OR)

  • **value:**指定拥有 /user/delete 权限才能访问,权限可以是多个,以逗号隔开
  • **logical:**有两个值,Logical.OR(只要拥有其中一个权限就访问),Logical.AND(需要拥有指定的全部权限才能访问,否则会抛出权限不足异常)
Thymeleaf 模板中使用 Shiro 权限标签

修改 thymeleaf 模板的 html 标签,加入 xmlns:shiro=”http://www.pollix.at/thymeleaf/shiro 命名空间:


常用的 Shiro 标签有以下:

  • shiro:hasRole=”root”:需要拥有root角色
  • shiro:hasAnyRoles=”root,guest”:需要拥有root和guest中的任意一个角色
  • :需要同时拥有root和guest角色
  • shiro:hasPerm:原理同上
  • shiro:hasAnyPerms :原理同上
  • shiro:hasAllPerms :原理同上
登录
  • 启动项目
  • 访问 http://localhost:8080
  • 用户名:root
  • 登录密码:123456
  • 文章作者:彭超
  • 本文首发于个人博客:https://antoniopeng.com/2019/06/14/springboot/SpringBoot%E6%95%B4%E5%90%88Shiro%E5%AE%9E%E7%8E%B0%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E4%B8%8E%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6/
  • 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 彭超 | Blog!
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号