昨天尝试了,spring boot和公司现有的boss系统整合。之前已经有一些模块用spring boot写了,所以这次想更彻底点。但是在整合的时候却遇到了麻烦,很烦恼。 因为我们原来的页面是用jsp写的,spring boot又强烈不推荐用jsp作为展示层。我用的ide是IDEA,jdk1.8,spring boot 1.4.3. 主要问题是在多模块中,项目启动后会有问题,表现为404错误,而我单个模块运行又是好的,我也是醉了,这个问题也不好定位。花了蛮多时间的,查阅了一些文章。但还是并没有解决。 于是我想到了我开发的时候就用原来的模式,但是我部署的时候用spring boot的方式,也就是说,开发和部署分离。(今天上午尝试了一下,并没有成功,问题是因为原来是用spring,spring-mvc来实现了,但是要用spring boot发布就会引入新的java文件和spring boot的相关jar包,即使我用profile划分也还是不可以。) 今天又尝试了一下,404的问题,被我解决了,虽然还是雨点懵,但是找到了解决问题的关键点,在于idea中spring boot的配置,working directory ,配置为 $MODULE_DIR$,(旁边有个快捷键可以选择)。虽然还有其他问题,但是离成功又近了一步。 还有就是在以后架构进化中想采用dubbo与spring boot结合的方式,因为这样更加便于部署。也跟方便整合。 今天尝试了另一种方法,把spring-shiro.xml转化为shiroConfig.java。结果成功了,虽然我也不知道到底是什么原因,但至少是有效的。这点还是不错的,毕竟研究了好几天,尝试过runable-war,但是感觉这种才是最方便的。 我把两种代码晒出来。
####这是spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<description>Shiro安全配置</description>
<!--安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义Realm-->
<property name="realm" ref="shiroDbRealm"/>
<!--将缓存管理器,交给安全管理器-->
<!--<property name="cacheManager" ref="shiroEhcacheManager"/>-->
<property name="cacheManager" ref="shiroCacheManager"/>
<property name="sessionManager" ref="sessionManager" />
<!-- 记住密码管理 -->
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<bean id="shiroCacheManager" class="com.sanjiang.boss.sys.secure.auth.cache.ShiroCacheManager"></bean>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO"/>
<property name="globalSessionTimeout" value="3600000"/>
<property name="sessionValidationInterval" value="3600000" />
<property name="sessionValidationSchedulerEnabled" value="true" />
<!--<property name="sessionIdCookie.name" value="BOSS_JSESSIONID"/>-->
<!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<property name="sessionIdCookie" ref="shareSession" />
</bean>
<!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<bean id="shareSession" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- cookie的name,对应的默认是 JSESSIONID -->
<constructor-arg name="name" value="SHAREJSESSIONID" />
<!-- jsessionId的path为 / 用于多个系统共享jsessionId -->
<property name="path" value="/" />
<property name="httpOnly" value="true"/>
</bean>
<!--<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO" >-->
<bean id="sessionDAO" class="com.mjm.boss.sys.secure.auth.session.ShiroSessionDAO" ></bean>
<!-- 項目自定义的Realm -->
<bean id="shiroDbRealm" class="com.mjm.boss.sys.secure.auth.realm.ShiroDbRealm"/>
<!--<bean id="shiroDbRealm" class="com.sanjiang.boss.portal.task.secure.auth.ShiroDbRealm"/>-->
<!-- 记住密码Cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<!-- 7天,采用spring el计算方便修改[细节决定成败]! -->
<property name="maxAge" value="#{7 * 24 * 60 * 60}"/>
</bean>
<!-- rememberMe管理器,cipherKey生成见{@code Base64Test.java} -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('5aaC5qKm5oqA5pyvAAAAAA==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 安全管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 默认的登陆访问url -->
<property name="loginUrl" value="/login"/>
<!-- 登陆成功后跳转的url -->
<property name="successUrl" value="/index"/>
<!-- 没有权限跳转的url -->
<property name="unauthorizedUrl" value="/unauth"/>
<property name="filters">
<util:map>
<entry key="membership" value-ref="membershipFilter"/>
<entry key="authc" >
<bean class="com.mjm.boss.sys.secure.auth.filter.ShiroFormAuthenticationFilter"></bean>
</entry>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
<!--
anon 不需要认证
authc 需要认证
user 验证通过或RememberMe登录的都可以
-->
/commons/** = anon
/static/** = anon
/webhooks = anon
/login = anon
/ic/** = anon
/excel/upload = anon
<!--
其他 系统 调用 easyreport 报表
-->
/report/** = anon
/assets/** = anon
/dynamic/** = anon
/o2o/** = anon
/** = user
</value>
</property>
</bean>
<!-- 用户授权信息Cache, 采用EhCache -->
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>
<!-- 在方法中 注入 securityManager ,进行代理控制 -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<!-- 启用shrio授权注解拦截方式 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<bean id="membershipFilter" class="com.mjm.boss.sys.secure.auth.filter.MembershipFilter"/>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory" />
<!--<bean id="casSubjectFactory" class="org.apache.shiro.mgt.SubjectFactory" />-->
<!-- shiro 的注解权限配置的异常跳转 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">/error/noAuthorization</prop>
<prop key="org.apache.shiro.authz.UnauthenticatedException">/error/noAuthentication</prop>
</props>
</property>
</bean>
</beans>
####这是ShiroConfiguration
/**
* Shiro 配置
* 这样配置后,就可以正常登陆了,不然会报@Autowried错误,应该是没有加载web.xml导致的问题吧
* 另一种解决方法是加载web.xml,不过有点复杂了
* Created by mjm on 2017/4/4.
*/
@Configuration
public class ShiroConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
/**
* EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
* @return
*/
@Bean(name = "shiroEhcacheManager")
public EhCacheManager getEhCacheManager() {
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return em;
}
/**
* HashedCredentialsMatcher,这个类是为了对密码进行编码的,防止密码在数据库里明码保存,当然在登陆认证的生活,这个类也负责对form里输入的密码进行编码。
* @return
*/
/* @Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(2);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}*/
/**
* ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
* @return
*/
@Bean(name = "shiroDbRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroDbRealm getShiroRealm() {
ShiroDbRealm realm = new ShiroDbRealm();
// realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
// return new ShiroDbRealm();
}
/**
* LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,负责org.apache.shiro.util.Initializable类型bean的生命周期的,
* 初始化和销毁。主要是AuthorizingRealm类的子类,以及EhCacheManager类。
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
* @return
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
/**
* SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
* @return
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager() {
DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
webSecurityManager.setRealm(getShiroRealm());
// <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
webSecurityManager.setCacheManager(getEhCacheManager());
return webSecurityManager;
}
/**
* AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
* 老实说,这里注入securityManager,我不知道有啥用,从source上看不出它在什么地方会被调用。
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(getDefaultWebSecurityManager());
return new AuthorizationAttributeSourceAdvisor();
}
/**
* 加载shiroFilter权限控制规则(从数据库读取然后配置)
*
* @author SHANHY
* @create 2016年1月14日
*/
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
/// 下面这些规则配置最好配置到配置文件中 ///
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
filterChainDefinitionMap.put("/user", "authc");// 这里为了测试,只限制/user,实际开发中请修改为具体拦截的请求规则
// anon:它对应的过滤器里面是空的,什么都没做
logger.info("##################从数据库读取权限规则,加载到shiroFilter中##################");
// filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取
// filterChainDefinitionMap.put("/login", "anon");
// filterChainDefinitionMap.put("/**", "anon");//anon 可以理解为不拦截
// filterChainDefinitionMap.put("/sa*//**", "authc");
// filterChainDefinitionMap.put("*//**", "anon");
//更好的方法是读取数据库或者配置文件,这样可以更加严格,主要是对于url的拦截
filterChainDefinitionMap.put("/commons/**","anon");
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/webhooks","anon");
filterChainDefinitionMap.put("/login","anon");
filterChainDefinitionMap.put("/ic/**","anon");
filterChainDefinitionMap.put("/excel/upload","anon");
// 其他 系统 调用 easyreport 报表
filterChainDefinitionMap.put("/report/**","anon");
filterChainDefinitionMap.put("/assets/**","anon");
filterChainDefinitionMap.put("/dynamic/**","anon");
filterChainDefinitionMap.put("/o2o/**","anon");
filterChainDefinitionMap.put("/**","user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}
/**
* 注册DelegatingFilterProxy(Shiro)
* 集成Shiro有2种方法:
* 1. 按这个方法自己组装一个FilterRegistrationBean(这种方法更为灵活,可以自己定义UrlPattern,
* 在项目使用中你可能会因为一些很但疼的问题最后采用它, 想使用它你可能需要看官网或者已经很了解Shiro的处理原理了)
* 2. 直接使用ShiroFilterFactoryBean(这种方法比较简单,其内部对ShiroFilter做了组装工作,无法自己定义UrlPattern,
* 默认拦截 /*)
*
*/
// @Bean
// public FilterRegistrationBean filterRegistrationBean() {
// FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
// filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
// // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
// filterRegistration.addInitParameter("targetFilterLifecycle", "true");
// filterRegistration.setEnabled(true);
// filterRegistration.addUrlPatterns("/*");// 可以自己灵活的定义很多,避免一些根本不需要被Shiro处理的请求被包含进来
// return filterRegistration;
// }
/**
* ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean
.setSecurityManager(getDefaultWebSecurityManager());
Map<String, Filter> filters = new LinkedHashMap<>();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login");
filters.put("logout", logoutFilter);
filters.put("membership",getMemberShipFilter());
filters.put("authc",getShiroFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
loadShiroFilterChain(shiroFilterFactoryBean);
return shiroFilterFactoryBean;
}
@Bean("membershipFilter")
public MembershipFilter getMemberShipFilter(){
return new MembershipFilter();
}
@Bean("formAuthenticationFilter")
public ShiroFormAuthenticationFilter getShiroFormAuthenticationFilter(){
return new ShiroFormAuthenticationFilter();
}
/**
* shiro 的注解权限配置的异常跳转
* @return
*/
@Bean("simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("org.apache.shiro.authz.UnauthorizedException","/error/noAuthorization");
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","/error/noAuthentication");
exceptionResolver.setExceptionMappings(properties);
return exceptionResolver;
}
/**
* 在方法中 注入 securityManager ,进行代理控制
* 加入,启动会报错
*/
/* @Bean("methodInvokingFactoryBean")
public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
methodInvokingFactoryBean.setArguments(new Object[]{"securityManager"});
return methodInvokingFactoryBean;
}*/
/**
* ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
* @return
*/
/* @Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}*/
}
围绕spring boot的生态日渐成熟,对于原来的项目想采用spring boot的开发的愿求也越来越强烈。特别想用spring cloud中的一些特性,这样才能更加更好的扩展起来。比如开发一个api网关,这样其他应用调用的api的时候就可以更好的把系统中api开放出来。