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

 

 

 

  

  

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM