Spring boot集成shiro使用Ajax方式,最详细教程

最近一直在自己的个人项目中集成进shiro这个权限控制框架,踩了不少的坑,sb(允许我这么叫他把,方便简洁)集成shiro的教程不少,但是使用ajax方式的还真的不是很多,下面把我自己的经验分享给大家。


1、在pom中加入shiro的包

 <!-- shiro权限控制 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

2、首先创建实体
这里写图片描述

一共是这三个实体

这里是UserEntity

package com.cy.example.entity;

import java.util.List;

import org.springframework.stereotype.Repository;

@Repository
public class UserEntity extends SuperEntity {

    private String c_username;

    private String c_pwd;

    private String c_phone;

    private String n_age;

    private String n_sex;

    private int n_status;

    private List<SysRoleEntity> roleList;// 一个用户具有多个角色


    //getter setter 省略,以下2个实体也是

    public byte[] getCredentialsSalt() {
        // TODO Auto-generated method stub
        return this.c_username.getBytes();
    }
}

SysRoleEntity

package com.cy.example.entity;

import java.util.ArrayList;
import java.util.List;

public class SysRoleEntity extends SuperEntity {

    private String c_roleName;

    private List<SysPermisEntity> permisList;// 一个角色对应多个权限

    private List<UserEntity> userList;// 一个角色对应多个用户


    public List<String> getPermissionsName() {
        List<String> list = new ArrayList<String>();
        List<SysPermisEntity> perlist = getPermisList();
        for (SysPermisEntity per : perlist) {
            list.add(per.getC_permisName());
        }
        return list;
    }

    @Override
    public String toString() {
        return "SysRoleEntity [c_roleName=" + c_roleName + ", permisList="
                + permisList + ", userList=" + userList + "]";
    }

}

SysPermisEntity

package com.cy.example.entity;

import java.util.List;

public class SysPermisEntity extends SuperEntity {

    private String c_permisName;

    private List<SysRoleEntity> roles;// 一个权限对应一个角色



}

3、数据库准备
这里写图片描述

这里多了2个表,一个用户关联角色表,一个是角色关联权限表
表结构和数据直接看sql吧,

DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c_permisName` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', 'add');
INSERT INTO `sys_permission` VALUES ('2', 'del');
INSERT INTO `sys_permission` VALUES ('3', 'update');
INSERT INTO `sys_permission` VALUES ('4', 'list');
INSERT INTO `sys_permission` VALUES ('5', 'user:list');
INSERT INTO `sys_permission` VALUES ('6', 'user:update');

-- ----------------------------
-- Table structure for sys_roles
-- ----------------------------
DROP TABLE IF EXISTS `sys_roles`;
CREATE TABLE `sys_roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c_roleName` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_roles
-- ----------------------------
INSERT INTO `sys_roles` VALUES ('1', 'admin');
INSERT INTO `sys_roles` VALUES ('2', 'manege');
INSERT INTO `sys_roles` VALUES ('3', 'normal');

-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `n_permission_id` bigint(20) NOT NULL,
  `n_role_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('3', '3', '1');
INSERT INTO `sys_role_permission` VALUES ('4', '4', '1');
INSERT INTO `sys_role_permission` VALUES ('5', '1', '2');
INSERT INTO `sys_role_permission` VALUES ('6', '2', '2');
INSERT INTO `sys_role_permission` VALUES ('7', '3', '2');
INSERT INTO `sys_role_permission` VALUES ('8', '4', '2');
INSERT INTO `sys_role_permission` VALUES ('9', '3', '3');
INSERT INTO `sys_role_permission` VALUES ('10', '1', '1');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `n_userId` int(11) NOT NULL,
  `n_roleId` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '8', '1');

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `c_username` varchar(255) NOT NULL,
  `c_pwd` varchar(255) NOT NULL,
  `c_phone` varchar(255) DEFAULT NULL,
  `n_age` int(11) NOT NULL,
  `n_sex` int(11) NOT NULL,
  `c_createDate` varchar(255) DEFAULT NULL,
  `n_creater` bigint(20) DEFAULT NULL,
  `c_updateDate` varchar(255) DEFAULT NULL,
  `n_updater` bigint(20) DEFAULT NULL,
  `n_deleted` int(11) DEFAULT NULL,
  `n_status` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('8', 'admin', 'c4ca4238a0b92382', '1', '12', '0', '2017-08-01 11:00:05', '8', '2017-09-23 10:47:57', '8', '0', '1');

4、编写ShiroConfig.java

package com.cy.example.config;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cy.example.filter.ShiroPermissionsFilter;
import com.cy.example.utils.AuthRealm;

/*
 * Shiro 配置
 */
@Configuration
public class ShiroConfig {

    private static final Logger logger = LoggerFactory
            .getLogger(ShiroConfig.class);

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters  
        //将自定义 的ShiroFilterFactoryBean注入shiroFilter
        filters.put("perms", new ShiroPermissionsFilter());

        // 必须设置SecuritManager  
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/lib/**", "anon");

        filterChainDefinitionMap.put("/index", "anon");

    //这个是登录验证的后台地址,这里把它过滤掉,让自己的控制层来验证
    filterChainDefinitionMap.put("/system/user/validate", "anon");
        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        // 这里自定义的权限拦截规则
        filterChainDefinitionMap.put("/system/*/add", "perms[add]");
        filterChainDefinitionMap.put("/system/*/delete", "perms[del]");
        // filterChainDefinitionMap.put("/system/*/list", "perms[list]");
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面,这个就是类似于登录界面
        shiroFilterFactoryBean.setLoginUrl("/index");
        // 登录成功后要跳转的链接
//      shiroFilterFactoryBean.setSuccessUrl("/main");

        // 未授权界面;
        // shiroFilterFactoryBean.setUnauthorizedUrl("/menu/403");
        shiroFilterFactoryBean
                .setFilterChainDefinitionMap(filterChainDefinitionMap);

        logger.info("--------------Shiro拦截器工厂类注入成功----------------");
        return shiroFilterFactoryBean;
    }

    /*
     * 配置自定义的权限登录器
     */
    @Bean
    public AuthRealm authRealm() {
        AuthRealm authRealm = new AuthRealm();
//      authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    /*
     * 配置核心安全事务管理器
     */
    @Bean
    public SecurityManager securityManager() {
        logger.info("--------------shiro安全事务管理器已经加载----------------");
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(authRealm());
        return manager;
    }
}

5、创建realm,这个就是类似于用来赋值的。。我是这么理解的,在这里用户和权限的赋值。

package com.cy.example.utils;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.cy.example.entity.SysPermisEntity;
import com.cy.example.entity.SysRoleEntity;
import com.cy.example.entity.UserEntity;
import com.cy.example.service.UserService;

public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    private static final Logger logger = LoggerFactory
            .getLogger(AuthRealm.class);

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // TODO Auto-generated method stub
        logger.info("--------------权限配置——授权----------------");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserEntity user = (UserEntity) principals.getPrimaryPrincipal();
        for (SysRoleEntity role : user.getRoleList()) {
            authorizationInfo.addRole(role.getC_roleName());
            for (SysPermisEntity p : role.getPermisList()) {
                authorizationInfo.addStringPermission(p.getC_permisName());
            }
        }
        logger.info(user.toString());
        return authorizationInfo;
    }

    /*
     * 认证信息.(身份验证) : Authentication 是用来验证用户身份 如果返回一个SimpleAccount
     * 对象则认证通过,如果返回值为空或者异常,则认证不通过。 1、检查提交的进行认证的令牌信息 2、根据令牌信息从数据源(通常为数据库)中获取用户信息
     * 3、对用户信息进行匹配验证 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例
     * 5、验证失败则抛出AuthenticationException异常信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        // TODO Auto-generated method stub
        logger.info("***用户身份验证");
        // 获取用户的输入的账号.
        String username = (String) token.getPrincipal();
        if (StringUtil.IsNullOrEmptyT(username)) {
            return null;
        }
        logger.info("***" + token.getCredentials());
        // 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        UserEntity user = userService.selectOne(new EntityWrapper<UserEntity>().eq("c_username", username));

        logger.info("***登录user:" + user);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user, // 用户名
                user.getC_pwd(), // 密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),// 这里的getCredentialsSalt()只是返回一个唯一值,我返回的是用户名,用来加密的
                salt=username+salt
                getName() // realm name
        );
        return authenticationInfo;
    }

}

这里的两个方法就是用来给用户和权限赋值的。


6、给大家看一下我的数据库查询的sql

<select id="findOneByUsername" parameterType="java.lang.String" resultMap="BaseResultMap" >
       SELECT
            u.id,
            c_username,
            u.c_pwd,
            u.c_phone,
            u.n_age,
            u.n_status,
            u.c_createDate,
            u.n_creater,
            u.c_updateDate,
            u.n_updater,
            CASE
        WHEN n_sex = 1 THEN
            '男'
        WHEN n_sex = 0 THEN
            '女'
        END AS n_sex,
        r.c_roleName,
        r.id as r_id,
        p.id as p_id,
        p.c_permisName
        FROM
            users u
        LEFT JOIN sys_user_role ur ON u.id = ur.n_userId
        LEFT JOIN sys_roles r ON ur.n_roleId = r.id
        LEFT JOIN sys_role_permission rp ON rp.n_role_id = ur.n_roleId
        LEFT JOIN sys_permission p ON p.id = rp.n_permission_id
       WHERE u.c_username = #{c_username} and n_deleted=0
    </select>

通过左连接把角色和权限查询出来
7、看一下验证登录的控制层

@SuppressWarnings("finally")
    @RequestMapping("/validate")
    @ResponseBody
    public Map<String, Object> validate(String username, String password) {
        Map<String, Object> map = new HashMap<String, Object>();
        password = MD5Util.GetMD5Code(password);
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                username, password);
        boolean flag = true;
        String msg = "";
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(usernamePasswordToken); // 完成登录
            UserEntity user = (UserEntity) subject.getPrincipal();
            subject.getSession().setAttribute(WebConfig.LOGIN_USER, user);
            LoginRecordEntity loginRecord = new LoginRecordEntity();
            loginRecord.setC_createDate(DateUtil.getNow());
            loginRecord.setC_loginIp(super.getIP(getRequest()));
            loginRecord.setC_username(user.getC_username());
            loginRecordService.add(loginRecord);
            msg = "登陆成功!";
            map.put("flag", flag);
        } catch (Exception exception) {
            if (exception instanceof UnknownAccountException) {
                logger.info("账号不存在: -- > UnknownAccountException");
                msg = "登录失败,用户账号不存在!";
            } else if (exception instanceof IncorrectCredentialsException) {
                logger.info(" 密码不正确: -- >IncorrectCredentialsException");
                msg = "登录失败,用户密码不正确!";
            } else {
                logger.info("else -- >" + exception);
                msg = "登录失败,发生未知错误:" + exception;
            }
            map.put("flag", false);
        } finally {
            map.put("msg", msg);
            return map;
        }
    }

返回数据的格式看一下map就知道了。


8、最重要的一步,添加权限验证失败的过滤器,当时搞这个权限失败JSON返回数据我搞了很久,走了不少弯路,希望大家能够成功的集成shiro

package com.cy.example.filter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cy.example.utils.JsonUtil;
import com.cy.example.utils.StringUtil;

public class ShiroPermissionsFilter extends PermissionsAuthorizationFilter {

    private static final Logger logger = LoggerFactory
            .getLogger(ShiroPermissionsFilter.class);

    /**
     * shiro认证perms资源失败后回调方法
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws IOException
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
        logger.info("----------权限控制-------------");
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String requestedWith = httpServletRequest.getHeader("X-Requested-With");
        if (!StringUtil.IsNullOrEmpty(requestedWith) &&
                StringUtil.IsEmpty(requestedWith, "XMLHttpRequest")) {//如果是ajax返回指定格式数据
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("flag", false);
            result.put("msg", "权限不足!");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            httpServletResponse.getWriter().write(JsonUtil.collectToString(result));
        } else {//如果是普通请求进行重定向
            httpServletResponse.sendRedirect("/403");
        }
        return false;
    }

}

下面附上我的项目地址,想看源码 的可以去下载,对你有帮助请star我的github地址

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
spring boot 实践学习案例,与其它组件结合如 mybatis、jpa、dubbo、redis、mongodb、memcached、kafka、rabbitmq、activemq、elasticsearch、security、shiro等 #### Spring Boot 版本 - 2.0.3.RELEASE #### 模块说明 - springboot-basic - Spring Boot 基础知识,包括SpringBoot起步、配置详解、aop、filter、拦截器、监听、启动器、全局异常处理、外部Tomcat启动、HTTPS、监控 等。 - springboot-data - Spring Boot 数据库操作,包括SpringJDBC、JPA、Mybatis注解版 & XML版、MongoDB。其中,每个版本都有其对应的多数据源解决方案。 - springboot-caches - Spring Boot 缓存,包括redis、ehcache、spring-cache、memcached、使用redis实现session共享 等。 - springboot-templates - Spring Boot 模板,包括thymeleaf、freemarker、jsp、表单校验 等。 - springboot-docs - Spring Boot 文档生成工具,包括 Swagger、Spring RestDocs - springboot-bussiness - Spring Boot 业务应用,包括 定时任务、上传文件、发送邮件、Doc文档操作 等。 - springboot-ajax - Spring Boot AJAX 跨域,包括 JSONP、Node.js与SpringBoot集成使用反向代理 等。 - springboot-websockets - Spring Boot 使用 Websocket - springboot-webflux - Spring Boot 集成 WebFlux 开发反应式 Web 应用 - springboot-dubbo - Spring Boot 集成 Dubbo 的三种方式 - springboot-search - Spring Boot 集成 搜索引擎,包括 elasticsearch、solr - springboot-mq - Spring Boot 集成 消息队列,包括 kafka、rabbitmq、activemq、rocketmq、redismq - springboot-auth - Spring Boot 权限认证,包括 Apache ShiroSpring Security - springboot-cloud - Spring Cloud 入门,包括 Eureka(服务注册与发现)、Config(配置中心)、Hystrix(断路器)、Bus(消息总线) 等
要在 Spring Boot使用 Shiro 1.10.0,您需要执行以下步骤: 1. 添加 Shiro 依赖项 在 pom.xml 文件中添加以下依赖项: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.6.0</version> </dependency> ``` 2. 配置 Shiro 在 application.properties 或 application.yml 文件中添加以下 Shiro 配置: ```yaml shiro: # 开启 Shiro 的注解支持,例如 @RequiresRoles, @RequiresPermissions 等 enabled: true # Shiro 过滤器链配置 filter-chain-definition-map: # 可以在此处添加自定义的过滤器链规则 # /user/** 表示对 user 目录下的所有请求进行拦截,authc 表示需要认证才能访问 /user/**: authc # Shiro Realm 配置 realms: # 可以在此处添加自定义的 Realm 类 # realm1 表示 Realm 类的名称,com.example.MyRealm 表示该类的全限定名称 realm1: class-name: com.example.MyRealm ``` 3. 编写 Shiro Realm 类 编写一个自定义的 Realm 类,继承 org.apache.shiro.realm.AuthorizingRealm 类,并重写 doGetAuthorizationInfo 和 doGetAuthenticationInfo 方法,实现权限和认证的逻辑。 ```java public class MyRealm extends AuthorizingRealm { // 认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 在此处编写认证逻辑 return null; } // 授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 在此处编写授权逻辑 return null; } } ``` 以上就是在 Spring Boot使用 Shiro 1.10.0 的基本步骤。需要注意的是,Shiro 的配置非常灵活,可以根据具体的需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值