欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springboot整合shiro

程序员文章站 2022-07-08 11:58:17
...

 

    因前后多个项目中使用的到springboot和shiro,多方查阅书籍和博客,在此做一个小小整理,以备后需。好记性不如烂笔头。

 

一、shiro介绍

二、springboot介绍

三、整合

1、添加依赖

 

在pom文件中新增shiro相关的依赖。缓存这里暂且采用ehcache。如果是用redis需要做小部分修改。

 

   <dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- shiro ehcache -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- shiro标签支持 -->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>2.0.0</version>
		</dependency>
    <!-- ehchache -->
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>

 2、shiro配置


springboot整合shiro
            
    
    博客分类: spring bootjava   除了以上,在shiroFilter中还需要配置at.pollux.thymeleaf.shiro.dialect.ShiroDialect,为了在thymeleaf里使用shiro的标签的bean

@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

 

 

 

3、ehcache的配置

 

1、首先得有一个ehcache的xml文件,ehcache.xml(内容略)放到/src/main/resource下,也就是类路径下。

 

2、需要在applicantion.yml文件中声明

 

spring:
  cache:
    ehcache:
      config: classpath:config/ehcache.xml

 

 3、启动类上需要加上@EnableCaching注解

项目启动时spring会自动构造net.sf.ehcache.CacheManager对象,在类属性中可以通过@Autowired注解得到该对象。

 然后将cacheManager注入到ehCacheManager中。

 

	@Autowired
	private CacheManager cacheManager;

	@Bean
	public EhCacheManager ehCacheManager() {
		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManager(cacheManager);
		return ehCacheManager;
	}

 

四、前后端分离导致的问题

 

 

1、前后端分离,后端只负责返json数据。

 

后端处理:

 

需要自定义一个配置类MyWebMvcConfig,实现

 

org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口。

 

MyWebMvcConfig里面可以添加很多mvc相关的配置。

 

 

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

	/**
	 * 以前写SpringMVC的时候,如果需要访问一个页面,必须要写Controller类,然后再写一个
         * 方法跳转到页面,感觉好麻烦,其实重写WebMvcConfigurerAdapter中的addViewControllers方法即可
         * 达到效果了
	 */
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		WebMvcConfigurer.super.addViewControllers(registry);
	}

	/**
	 * 添加类型转换器和格式化器
	 * 
	 * @param registry
	 */
	@Override
	public void addFormatters(FormatterRegistry registry) {
	}

	/**
	 * 跨域支持(主要看这个)
	 * 
	 * @param registry
	 */
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		 List<String> allowedOrigins = new ArrayList<>();
	        allowedOrigins.add("http://localhost:8983");//前端访问地址
	        String[] objects = allowedOrigins.toArray(new String[allowedOrigins.size()]);
		registry.addMapping("/**")
				.allowCredentials(true) //允许Cookie跨域,在做登录校验的时候有用
				.allowedMethods("GET", "POST", "DELETE", "PUT")//允许提交请求的方法,*表示全部允许
				.maxAge(3600 * 24)//预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
		        .allowedOrigins(objects)//允许向该服务器提交请求的URI,*表示全部允许
		        .allowedHeaders("*");  //允许的头信息,*标识全部允许
	}

	/**
	 * 添加静态资源--过滤
	 * 
	 * @param registry
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
	}

	/**
	 * 配置消息转换器
	 * 
	 * @param converters
	 */
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		
	}

	/**
	 * 配置拦截器
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// TestInterceptor extends HandlerInterceptorAdapter
		// registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
	}

}
 

 

2、或者另外一种方式:

 

自定义一个过滤器,添加相关跨域处理

 

 

@Component
public class CorsFilter implements Filter {

	final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletResponse response = (HttpServletResponse) res;
		HttpServletRequest reqs = (HttpServletRequest) req;
		response.setHeader("Access-Control-Allow-Origin", reqs.getHeader("Origin"));
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		response.setHeader("Access-Control-Max-Age", "3600");
		response.setHeader("Access-Control-Allow-Headers", "x-requested-with,APPID,token");
		response.setHeader("Access-Control-Expose-Headers", "APPID");//允许前端跨域获取的头
		chain.doFilter(req, res);
	}

	public void init(FilterConfig filterConfig) {
		
	}

	public void destroy() {
	}
}
 

 

 

2、前端

 

 

function loginOn(){
var data_={
username:"admin",
password:"admin"
}
$.ajax({
    type: "POST",
    url: "http://192.168.2.240:8091/login",
	data:data_,
    xhrFields: {
        withCredentials: true // 携带跨域cookie
    },
    /*processData: false,*/  //测试时,这个如果带上,返回头Response里面会拿不到Cookie,Cookie里面存放了sessionid
    success: function(data) {
        console.log(data);  
    }
});
}
 

 

3、好的!现在登录正常了。

 

但是在shiro的配置里面,我们有关于登录页的配置

 

 

	@Bean
	ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		shiroFilterFactoryBean.setLoginUrl("/login");
     .....
}
 

 

shiro发现用户没有登录时,会重定向到/login页面。但是,因为前后端是分离的。所以得给前端返一个未登录

 

的json消息。

 

跨域重定向不好处理,貌似jsonp可以解决。这个重定向,返回的header里面有一个

 

Location... 后面跟的是重定向的地址,浏览器是不会去处理的。

 

所以。需要重写一些东西。我只重写了量个过滤器:

 

FormAuthenticationFilter和LogoutFilter

 

并将ShiroFilterFactoryBean里面注册的filter替换成自己的。

 

MyFormAuthenticationFilter

 

 

@Configuration
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
	private static final Logger log = LoggerFactory.getLogger(MyFormAuthenticationFilter.class);

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		if (isLoginRequest(request, response)) {
			if (isLoginSubmission(request, response)) {
				if (log.isTraceEnabled()) {
					log.trace("Login submission detected.  Attempting to execute login.");
				}
				return executeLogin(request, response);
			} else {
				if (log.isTraceEnabled()) {
					log.trace("Login page view.");
				}
				// allow them to see the login page ;)
				return true;
			}
		} else {
			if (log.isTraceEnabled()) {
				log.trace("Attempting to access a path which requires authentication.  Forwarding to the "
						+ "Authentication url [" + getLoginUrl() + "]");
			}
			// 认证未通过json返回
			HttpServletResponse res = (HttpServletResponse) response;
			HttpServletRequest req = (HttpServletRequest) request;
			// 登出json返回
			res.setCharacterEncoding("UTF-8");
			res.setHeader("Content-type", "application/json;charset=UTF-8");
			res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
			res.setHeader("Access-Control-Allow-Credentials", "true");
			res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
			ResponseMsg httpResponse = ResponseMsg.create(Code.NotLogin);
			res.getWriter().write(JSONObject.toJSONString(httpResponse));
			return false;
		}
	}

}
 

 

之前是想在方法里抛出一个自定义的异常类BizException extends RuntimeException。但是发现自定义的

 

MyExceptionHandler implements HandlerExceptionResolver没法拦截处理到我的异常。所以直接在方法

 

里,对response做了处理。

 

同理MyLogoutFilter

 

 

public class MyLogoutFilter extends LogoutFilter {

	@Override
	protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl)
			throws Exception {
		HttpServletResponse res = (HttpServletResponse) response;
		HttpServletRequest req = (HttpServletRequest) request;
		// 登出json返回
		res.setCharacterEncoding("UTF-8");
		res.setHeader("Content-type", "application/json;charset=UTF-8");
		res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
		res.setHeader("Access-Control-Allow-Credentials", "true");
		res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		ResponseMsg httpResponse = ResponseMsg.create(Code.OK);
		res.getWriter().write(JSONObject.toJSONString(httpResponse));
	}

}
 

 

 

最后替换:

	@Bean
	ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
		filters.put("authc", new MyFormAuthenticationFilter());//将自定义 的MyFormAuthenticationFilter注入shiroFilter中  
		filters.put("logout", new MyLogoutFilter());//将自定义 的MyLogoutFilter注入shiroFilter中  
		
		shiroFilterFactoryBean.setSecurityManager(securityManager);
.....
}

 

4、完成。

 

 

  • springboot整合shiro
            
    
    博客分类: spring bootjava  
  • 大小: 222.6 KB