前后端分离 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好了。

相关推荐