前后端分离 springboot整合shiro

  1. 实现前后端的跨域,我是在后端配置类里实现
  2. 创建配置类 WebMvcConfig
  3. import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    // 配置全局跨域
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("http://localhost:8081")
                    .allowedMethods("*")
                    .allowedHeaders("*")
                    .allowCredentials(true);
        }
    }
  4. Shiro的配置
  5. 创建配置类 ShiroConfig
  6. import cn.xej.util.MyPassThruAuthenticationFilter;
    import cn.xej.util.MyRealm;
    import cn.xej.util.MySessionManager;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import javax.servlet.Filter;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.SessionManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ShiroConfig {
    
        /**
         * 请求拦截
         * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            Map<String, Filter> filtersMap = new HashMap<>();
            MyPassThruAuthenticationFilter authFilter = new MyPassThruAuthenticationFilter();
            filtersMap.put("authc", authFilter);
            shiroFilterFactoryBean.setFilters(filtersMap);
            // 拦截器.
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
            filterChainDefinitionMap.put("/doLogin", "anon");
            filterChainDefinitionMap.put("/**", "authc");
    //        shiroFilterFactoryBean.setLoginUrl("/unauth");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        /**
         * @Title: securityManager
         * @Description: SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理
         * @return SecurityManager
         */
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm());
            securityManager.setSessionManager(sessionManager());
            return securityManager;
        }
    
        /**
         * 自定义认证
         * @Title: myShiroRealm
         * @Description: ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理
         * @return MyShiroRealm
         */
        @Bean
        public MyRealm myRealm() {
            MyRealm myRealm = new MyRealm();
            myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return myRealm;
        }
    
        /**
         * 密码凭证匹配器,作为自定义认证的基础 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
         *
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher() {
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
            hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于 md5(md5(""));
            hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
            return hashedCredentialsMatcher;
        }
    
        /**
         * 自定义sessionManager,用户的唯一标识,即Token或Authorization的认证
         */
        @Bean
        public SessionManager sessionManager() {
            MySessionManager mySessionManager = new MySessionManager();
            return mySessionManager;
        }
    
    }
    
  7. 创建MySessionManager类
  8. import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.apache.shiro.web.util.WebUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.Serializable;
    
    public class MySessionManager extends DefaultWebSessionManager{
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
        private static final String AUTHORIZATION = "Authorization";
        private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    
        public MySessionManager() {
            super();
        }
    
        @Override
        public Serializable getSessionId(ServletRequest request, ServletResponse response) {
            //前端ajax的headers中必须传入Authorization的值
            String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
            //如果请求头中有 Authorization 则其值为sessionId
            if (!StringUtils.isEmpty(id)) {
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                logger.info("sessionId="+id);
                return id;
            } else {
                //否则按默认规则从cookie取sessionId
                logger.info("sessionID为"+id);
                return super.getSessionId(request, response);
            }
        }
    }  
  9. 过滤器MyPassThruAuthenticationFilter
  10. import org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter;
    import org.apache.shiro.web.util.WebUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class MyPassThruAuthenticationFilter extends PassThruAuthenticationFilter{
        private Logger log = LoggerFactory.getLogger(this.getClass());
    
        //获取请求方法,若为OPTIONS请求直接返回True放行
        @Override
        public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
                log.info("OPTIONS方法直接返回True");
                return true;
            }
            return super.onPreHandle(request, response, mappedValue);
        }
    
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            HttpServletResponse httpResp = WebUtils.toHttp(response);
            HttpServletRequest httpReq = WebUtils.toHttp(request);
    
            /** 系统重定向会默认把请求头清空,这里通过拦截器重新设置请求头,解决跨域问题 */
            httpResp.addHeader("Access-Control-Allow-Origin", httpReq.getHeader("Origin"));
            httpResp.addHeader("Access-Control-Allow-Headers", "*");
            httpResp.addHeader("Access-Control-Allow-Methods", "*");
            httpResp.addHeader("Access-Control-Allow-Credentials", "true");
    
            if (isLoginRequest(request, response)) {
                return true;
            } else {
                saveRequestAndRedirectToLogin(request, response);
                return false;
            }
        }
    
    } 
  11. 前端我是使用jquery
  12. $.ajax({
                url: base + ‘/doLogin‘,
                type: "post",
                data: {
                    username: username,
                    password: password
                },
                dataType: ‘json‘,
                success: function (data) {
                    if (data.status === 200) {
                        alert(data.message);
                        sessionStorage.setItem("sessionId",data.data);
                        window.location = ‘main.html‘;
                    } else {
                        alert("失败");
                        location.reload();
                    }
                }
            }) 
  13. $.ajax({
        type: "GET",
        url: base + ‘/main‘,
        dataType: ‘json‘,
        beforeSend: function (xhr) {
          xhr.setRequestHeader("Authorization",sessionId)
        },
        success: function (data) {
          if(data.status === 200) {
            $("#info").html(data.message);
          }
        }
    })   
  14. 前面的beforeSend是设置请求头,一定要加。
  15. 后台控制器用户登录
  16. @PostMapping("/doLogin")
        public BaseResult doLogin(HttpServletRequest request, @RequestParam("username")String username, @RequestParam("password")String password){
            System.out.println(username);
            System.out.println(password);
    
            Map<String, String> map = new HashMap<String, String>();
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    
            try {
                subject.login(token);
                System.out.println(subject.getSession().getId());
                String sessionId = (String)subject.getSession().getId();
                System.out.println("777" + subject.getSession().getId());
    
                return BaseResult.success("登录成功!",sessionId);
            }catch (UnknownAccountException e){
                System.out.println("账号不存在");
                return BaseResult.fail("失败");
            }catch (AccountException e){
                System.out.println("密码错误");
                return BaseResult.fail("失败");
            }
        } 
  17. @GetMapping("/main")
        public BaseResult Main(HttpServletRequest request, HttpServletResponse response){
            System.out.println("***********");
    
            MySessionManager mySessionManager = new MySessionManager();
            Serializable sessionId = (Serializable) mySessionManager.getSessionId(request,response);
            System.out.println(sessionId);
    
            Subject requestSubject = new Subject.Builder().sessionId(sessionId).buildSubject();
            User user = (User) requestSubject.getPrincipal();
            List<Role> roleList = userService.getRoleByUserId(user.getUid());
            List<Permission> permissionList = null;
    
            for (Role r : roleList){
                System.out.println(r.getRoleName());
                System.out.println("角色id为:"+r.getRid());
                permissionList = permissionService.getPermissionByRoleId(r.getRid());
            }
            return BaseResult.success("成功",permissionList);
    
        }
  18. 前后端跨域,以及整合shiro好了。

  

  

原文地址:https://www.cnblogs.com/Alida/p/11995162.html

时间: 2024-09-28 19:58:41

前后端分离 springboot整合shiro的相关文章

在前后端分离的SpringBoot项目中集成Shiro权限框架

项目背景 公司在几年前就采用了前后端分离的开发模式,前端所有请求都使用ajax.这样的项目结构在与CAS单点登录等权限管理框架集成时遇到了很多问题,使得权限部分的代码冗长丑陋,CAS的各种重定向也使得用户体验很差,在前端使用vue-router管理页面跳转时,问题更加尖锐.于是我就在寻找一个解决方案,这个方案应该对代码的侵入较少,开发速度快,实现优雅.最近无意中看到springboot与shiro框架集成的文章,在了解了springboot以及shiro的发展状况,并学习了使用方法后,开始在网上

SpringSecurity解决跨域问题,在SpringBoot整合SprinSecurity中如何用前后端分离Ajax登录,Ajax登录返回状态200还是近error

先说说SpringSecurity如何实现前后端分离Ajax登录? 今天使用SpringBoot整合SpringSecurity中想使用Ajax替代SpringSecurit的Form表单提交,在这里我们的提交方式还是使用表单提交 http.formLogin().loginProcessingUrl("/authentication/form") loginProcessingUrl方法表示你登录请求的地址,在这里SpringSecurity默认登录页面地址是/login ,填写了u

一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器

一.前言 最近花了一个月时间完成了一套基于Spring Boot+Vue+Shiro前后端分离的代码生成器,目前项目代码已基本完成 止步传统CRUD,进阶代码优化: 该项目可根据数据库字段动态生成 controller.mapper.service.html.jsp.vue.php..py ... 等各种类型代码,采用 velocity 模板引擎在页面动态配置生成代码,前后端动态权限配置,前端权限精确到 按钮 级别,后端权限精确到 uri 上,QQ授权第三方单用户登录...等 基本环境: JDK

springboot shiro 前后端分离,解决跨域、过虑options请求、shiro管理session问题、模拟跨域请求

一.解决跨域.过虑options请求问题 1.创建过虑类 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; im

springboot2.0整合springsecurity前后端分离进行自定义权限控制

在阅读本文之前可以先看看springsecurity的基本执行流程,下面我展示一些核心配置文件,后面给出完整的整合代码到git上面,有兴趣的小伙伴可以下载进行研究 使用maven工程构建项目,首先需要引入最核心的依赖, <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <

JEECG-Boot 项目介绍——基于代码生成器的快速开发平台(Springboot前后端分离)

Jeecg-Boot 是一款基于代码生成器的智能开发平台!采用前后端分离架构:SpringBoot,Mybatis,Shiro,JWT,Vue&Ant Design.强大的代码生成器让前端和后台代码一键生成,不需要写任何代码,保持jeecg一贯的强大,绝对是全栈开发福音!! JeecgBoot在提高UI能力的同时,降低了前后分离的开发成本,JeecgBoot还独创在线开发模式(No代码概念),一系列在线智能开发:在线配置表单.在线配置报表.在线图表设计.在线设计流程等等. JEECG宗旨是: 简

Spring Boot + Shiro 实现前后端分离、权限控制

本文总结自实习中对项目的重构.原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelAndView,逐渐有了前后端分离的想法,由于之前,没有接触过,主要参考的还是网上的一些博客教程等,初步完成了前后端分离,在此记录以备查阅. 一.前后端分离思想 前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据.前后端之间完全通过public A

Springboot2 Vue 前后端分离 整合打包 docker镜像

项目使用springboot2和Vue前后端分离开发模式,再整合,容器化部署. 主要说明下大体的流程,扫除心里障碍,期间遇到的问题请自行解决. 首先说下Vue打包: 1.在Vue项目目录下运行命令打包:npm run build:prod --report 生成需要使用的dist文件,打包后会出现在项目目录下.(目录结构可能会不同) 按照如下方式整合到springboot项目中,resources在main目录下. (结构不同的话)一样拆到static目录下,static下面直接跟img.css

Session(数据)共享的前后端分离Shiro实战

1,前言 本文期望描述如何使用Shiro构建基本的安全登录和权限验证.本文实战场景有如下特殊需求:1,在集群和分布式环境实现session共享:2,前端只使用HTML/CSS/JS.因此无法直接使用Shiro提供的SessionManager,以及Shiro针对web应用提供的Filter拦截方式.当然,除非是一定要通过共享缓存的方式共享session,否则还是使用Shiro默认的session管理,毕竟增加独立缓存就意味着维护成本的提高和可用性的下降. 2, Shiro架构 首先一睹官方给出的