shiro整合springboot前后端分离

1、shiro整合springboot的配置

package com.hisi.config;

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

import javax.servlet.Filter;

import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.hisi.shiro.LoginAuthorizationFilter;
import com.hisi.shiro.RestFilter;
import com.hisi.shiro.UserRealm;

/**
 * shiro权限管理的配置
 * @author xuguoqin
 * @date 2018年5月4日
 * @version 1.0
 */
@Configuration
public class ShiroConfig {

	/**
	 * 安全管理器
	 * @param realm
	 * @return
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(userRealm());
		securityManager.setSessionManager(sessionManager());
		return securityManager;
	}
	
	/**
	 * Realm配置
	 * @return
	 */
	@Bean
	public UserRealm userRealm(){
		return new UserRealm();
	}
	
	/**
	 * SessionDAO配置
	 * @return
	 */
	@Bean
	public SessionDAO sessionDAO(){
		return new MemorySessionDAO();
	}
	
	/**
	 * sessionManager配置
	 * @param sessionDAO
	 * @return
	 */
	@Bean
	public DefaultWebSessionManager sessionManager(){
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(sessionDAO());
		return sessionManager;
	}
	
	/**
	 * shiroFilter配置
	 * @param securityManager
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){

		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(securityManager());
		Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
		filters.put("token", new LoginAuthorizationFilter());
		filters.put("corsFilter", new RestFilter());
		shiroFilter.setFilters(filters);
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		filterChainDefinitionMap.put("/user/login", "corsFilter,anon");
		filterChainDefinitionMap.put("/user/logout", "corsFilter,anon");
		filterChainDefinitionMap.put("/user/**", "corsFilter,token");
		shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilter;
	}
	
	/**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式,AOP式方法级权限检查
     */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") //依赖其他bean的初始化
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }
	
	/**
	 * 加入注解的使用,不加入这个注解不生效 使用shiro框架提供的切面类,用于创建代理对象 
	 * @param securityManager
	 * @return
	 */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
    
}


2、这里配置的两个过滤器RestFilter和LoginAuthorizationFilter,RestFilter是用于解决前后端分离时的跨域问题,服务端在响应头设置可以接受的请求参数

package com.hisi.shiro;

import java.io.IOException;
import java.util.Optional;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 前后端分离RESTful接口过滤器
 * 
 * @author xuguoqin
 *
 */
public class RestFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = null;
		if (request instanceof HttpServletRequest) {
			req = (HttpServletRequest) request;
		}
		HttpServletResponse res = null;
		if (response instanceof HttpServletResponse) {
			res = (HttpServletResponse) response;
		}
		if (req != null && res != null) {
			//设置允许传递的参数
			res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
			//设置允许带上cookie
			res.setHeader("Access-Control-Allow-Credentials", "true");
			String origin = Optional.ofNullable(req.getHeader("Origin")).orElse(req.getHeader("Referer"));
			//设置允许的请求来源
			res.setHeader("Access-Control-Allow-Origin", origin);
			//设置允许的请求方法
			res.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
		}
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {

	}

}

前者ajax请求的时候应该带上参数

$.ajax({
type: "GET",
url: url,
xhrFields: {
    withCredentials: true // 携带跨域cookie
},
processData: false,
success: function(data) {
    console.log(data);  
}
});


3、LoginAuthorizationFilter主要是对未登录的用户进行过滤然后返回json数据给前端,之前遇到的问题就是shiro配置的loginUrl会导致出现302的问题,在前后端分离的项目中,页面的跳转应该由前端来进行控制,这里前端使用的是vue框架,我需要对shiro中未登录的过滤器FormAuthenticationFilter进行重构

package com.hisi.shiro;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;

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

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONObject;
import com.commons.model.YfpjResult;
import com.hisi.mapper.HisiUserMapper;
import com.hisi.model.HisiUser;
import com.hisi.util.Constant;
import com.hisi.util.UserAuthStatusEnum;

/**
 * shiro未登录反回状态码
 * @author xuguoqin
 * @date 2018年5月10日
 * @version 1.0
 */
public class LoginAuthorizationFilter extends FormAuthenticationFilter {

	/**
	 * 这个方法是未登录需要执行的方法
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request,  
            ServletResponse response) throws IOException {
		
		HttpServletRequest httpRequest = (HttpServletRequest) request;  
        HttpServletResponse httpResponse = (HttpServletResponse) response;  
  
        Subject subject = getSubject(request, response);  
        if (subject.getPrincipal() == null) {  
	        //设置响应头
		httpResponse.setCharacterEncoding("UTF-8");
		httpResponse.setContentType("application/json");
		//设置返回的数据
		YfpjResult result = YfpjResult.build(UserAuthStatusEnum.UNLOGIN.getCode(), UserAuthStatusEnum.UNLOGIN.getMsg());
		//写回给客户端
		PrintWriter out = httpResponse.getWriter();
		out.write(JSONObject.toJSONString(result));
		//刷新和关闭输出流
		out.flush();
		out.close();
        } else {  
        	//设置响应头
	    	httpResponse.setCharacterEncoding("UTF-8");
	    	httpResponse.setContentType("application/json");
	    	//设置返回的数据
	    	YfpjResult result = YfpjResult.build(UserAuthStatusEnum.UNAUTH.getCode(), UserAuthStatusEnum.UNAUTH.getMsg());
	    	//写回给客户端
	    	PrintWriter out = httpResponse.getWriter();
	    	out.write(JSONObject.toJSONString(result));
	    	//刷新和关闭输出流
	    	out.flush();
	    	out.close();
        }  
        return false;  
	}
}

4.以后在进行前后端分离的项目开发的时候,可以前端封装一个允许带cookie的ajax请求,同时封装一个统一的未登录或者未授权状态码的判断

猜你喜欢

转载自blog.csdn.net/qq_31489805/article/details/80267916