Servlet 整合 shiro 日记

因工作需要引入shiro。但系统是基于Servlet3.0 ,而网上关于shiro 的教程大多是spring,因此记录下。

servlet 引入shiro:

1.引入shiro.jar:

shiro-core-1.4.0.jar

shiro-web-1.4.0.jar

shiro-ehcache-1.4.0.jar

 shiro+servlet 主要通过配置shiro.ini 进行搭建。

2.shiro.ini

[main]
securityManager=demo.CustomizeWebSecurityManager

[roles]
doctor=*
worker=*
patient=*

[urls]
/** = anon

 

3.Listener 和filter 直接使用servlet3.0的注解方式引入。注意shirofilter引入对原系统filter链影响。

Listener:

import org.apache.shiro.web.env.WebEnvironment;

/**
 * 
 * @author yuyi
 *
 */
@WebListener
public class EnvironmentLoaderListener extends org.apache.shiro.web.env.EnvironmentLoaderListener {
	@Override
}

Filter:

@WebFilter(urlPatterns = "/*"})
public class F1ShiroFilter extends org.apache.shiro.web.servlet.ShiroFilter {
}

 

无状态token验证

参考:
https://www.cnblogs.com/hujunzheng/p/7210157.html

思路是在Filter 里设置Realm验证所需的AuthenticationToken。同时停用shiro默认的servlet - session。参考里思路说得很清楚,就不多对思路累述。

 

关闭servlet默认的session:

public class CustomizeWebSecurityManager extends org.apache.shiro.web.mgt.DefaultWebSecurityManager {
	public CustomizeWebSecurityManager() {
		super(new CustomizeRealm());
		// 设置authenticator
		ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
		authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
		setAuthenticator(authenticator);

		// 设置authorizer
		ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
		authorizer.setPermissionResolver(new WildcardPermissionResolver());
		setAuthorizer(authorizer);

		// 设置取消session
		setSessionManager(new DefaultSessionManager() {
			{
				setSessionValidationSchedulerEnabled(false);
			}
		});
		setSubjectFactory(new StatelessDefaultSubjectFactory());
		setSubjectDAO(new DefaultSubjectDAO() {
			{
				setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator() {
					{
						setSessionStorageEnabled(false);
					}
				});
			}
		});

		List<Realm> realms = new ArrayList<>();
		realms.add(new CustomizeRealm());
		// 设置Realm
		setRealms(realms);
		SecurityUtils.setSecurityManager(this);
	}
}
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
	@Override
	public Subject createSubject(SubjectContext context) {
		// 不创建session
		context.setSessionCreationEnabled(false);
		return super.createSubject(context);
	}
}
public class CustomizeFormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {

	/**
	 * 获得登录token
	 * 
	 * @param request
	 * @param res
	 * @return
	 */
	protected String _GetCookieID(HttpServletRequest request, HttpServletResponse res) {
		// 兼容API接口参数传递
		String ercloud_id = request.getParameter("token");
		if (ercloud_id != null)
			return ercloud_id;
		Cookie[] cookies = request.getCookies();// 这样便可以获取一个cookie数组

		if (cookies == null)
			return null;

		for (Cookie cookie : cookies) {
			if (cookie.getName().equals(CookieEnum.token.toString())) {
				ercloud_id = StringUtils.isNotBlank(cookie.getValue()) ? cookie.getValue() : ercloud_id;
			}
		}
		return ercloud_id;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		try {
			// 生成无状态Token
			// 委托给Realm进行登录
			getSubject(request, response).login(new StatelessToken(
				_GetCookieID((HttpServletRequest) request, (HttpServletResponse) response), CRealm.doctor));//通过realm name 提交给对应的realm进行验证。
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

}
public class ModularRealmAuthenticator extends org.apache.shiro.authc.pam.ModularRealmAuthenticator {

    /**
     * 设置验证策略
     */
	@Override
	protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
			throws AuthenticationException {
		// 判断getRealms()是否返回为空
		assertRealmsConfigured();
		StatelessToken statelessToken = (StatelessToken) authenticationToken;

		Collection<Realm> realms = getRealms();
		// 登录类型对应的所有Realm
		Collection<Realm> typeRealms = new ArrayList<>();
		for (Realm realm : realms) {
			if (realm.getName().contains(statelessToken.getRealm().toString()))
				typeRealms.add(realm);
		}
		if (typeRealms.size() == 1) {
			return doSingleRealmAuthentication(typeRealms.iterator().next(), authenticationToken);
		} else {
			return doMultiRealmAuthentication(typeRealms, authenticationToken);
		}
	}
}

上面的重点是在于filter的整合。最简单的是直接在shiro.ini中进行配置:

[main]
#定义一个filter
filterdoctor= demo.CustomizeFormAuthenticationFilter


[urls]
#authc
/** = filterdoctor, roles[doctor]

如此非servlet-session的shiro 验证就可以了。

 

shiro动态请求配置。

参考:

https://www.w3cschool.cn/shiro/h5it1if8.html

实现


数据库:

175617_Eog4_3632223.png

修改shiro listener :

@WebListener
public class EnvironmentLoaderListener extends org.apache.shiro.web.env.EnvironmentLoaderListener {
	@Override
	public WebEnvironment initEnvironment(ServletContext servletContext) throws IllegalStateException {
		servletContext.setInitParameter("shiroEnvironmentClass", "demo.IniWebEnvironment");//使用自己的shiro.ini 解析类
		return super.initEnvironment(servletContext);
	}
}

 

IniWebEnvironment

public class IniWebEnvironment extends org.apache.shiro.web.env.IniWebEnvironment {
	private final static Logger log = LoggerFactory.getLogger(IniWebEnvironment.class);
	public static IniWebEnvironment iniWebEnvironment;

	@Override
	public void init() {
		super.init();
		iniWebEnvironment = this;
		iniWebEnvironment.refash();
	}

	public void refash() {
		PathMatchingFilterChainResolver p = (PathMatchingFilterChainResolver) getFilterChainResolver();
		DefaultFilterChainManager f = (DefaultFilterChainManager) p.getFilterChainManager();
		f.getFilters().clear();//清楚filter
		f.getChainNames().clear();//清楚url与filter之间的映射
		RefashFilterChain refashFilterChain = new RefashFilterChain();
		refashFilterChain.filters.forEach((k, v) -> {
			f.addFilter(k, v);
		});

		refashFilterChain.chains.forEach((k, v) -> {
			f.createChain(k, v);
		});
		log.info(" FilterChain refash OK");
	}

	class RefashFilterChain {
		@SuppressWarnings("serial")
		RefashFilterChain() {
			List<Map<String, Object>> list = QueryHelper.query("select * from urlchainmanage");

			if (list.isEmpty()) {
				list.add(new HashMap<String, Object>() {
					{
						put("chain", "/*");
						put("chaindefinition", "anon");
						put("filterclz", "org.apache.shiro.web.filter.authc.AnonymousFilter");
					}
				});
			}

			list.forEach(a -> {

				if (a.containsKey("chain") && StringUtils.isNotBlank((String) a.get("chain")))
					chains.put(StringUtils.trim((String) a.get("chain")),
							StringUtils.trim((String) a.get("chaindefinition")));

				if (a.containsKey("filterclz") && StringUtils.isNotBlank((String) a.get("filterclz"))) {
					List<String> filterclz = Arrays.asList(((String) a.get("filterclz")).split(","));
					String[] chaindefinitions = ((String) a.get("chaindefinition")).split(",");
					String chaindefinition;
					for (int i = 0; i < filterclz.size(); i++) {
						if (StringUtils.isNotBlank(filterclz.get(i))) {
							if (chaindefinitions.length <= i) {
								log.error(" 该数据错误:{}", a);
								return;
							}
							int j = -1;
							chaindefinition = chaindefinitions[i];
							if ((j = chaindefinition.indexOf("[")) != -1)
								chaindefinition = chaindefinition.substring(0,
										(j != -1 ? j : chaindefinition.length()));
							try {
								filters.put(StringUtils.trim(chaindefinition), (Filter) ClassLoaderUtil
										.loadClass(StringUtils.trim(filterclz.get(i))).newInstance());
							} catch (InstantiationException | IllegalAccessException e) {
								log.error(" filter load error :{}  ", filterclz.get(i), e);
							}
						}
					}
				}
			});
		}

		public Map<String, Filter> filters = new HashMap<String, Filter>();
		public Map<String, String> chains = new HashMap<String, String>();
	}

}

思路:

 org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver  implements FilterChainResolver 为shiro的url映射filter方法接口,那么修改这个接口返回的filter就能实现自定义设置请求配置。

通过查看源码,PathMatchingFilterChainResolver.getFilterChainManager() = org.apache.shiro.web.filter.mgt.DefaultFilterChainManager 中实际存放了filter和请求映射集

public class DefaultFilterChainManager implements FilterChainManager {
    private Map<String, Filter> filters; //pool of filters available for creating chains
    private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain
}

在org.apache.shiro.web.env.EnvironmentLoaderListener  extends org.apache.shiro.web.env. EnvironmentLoader 中,指明可以设置继承于WebEnvironment的shiro初始类。

    /**
     * Return the WebEnvironment implementation class to use, based on the order of:
     * <ul>
     *     <li>A custom WebEnvironment class - specified in the {@code servletContext} {@link #ENVIRONMENT_ATTRIBUTE_KEY} property</li>
     *     <li>{@code ServiceLoader.load(WebEnvironment.class)} - (if more then one instance is found a {@link ConfigurationException} will be thrown</li>
     *     <li>A call to {@link #getDefaultWebEnvironmentClass()} (default: {@link IniWebEnvironment})</li>
     * </ul>
     *
     * @param servletContext current servlet context
     * @return the WebEnvironment implementation class to use
     * @see #ENVIRONMENT_CLASS_PARAM
     * @param servletContext the {@code servletContext} to query the {@code ENVIRONMENT_ATTRIBUTE_KEY} property from
     * @return the {@code WebEnvironment} to be used
     */
    protected WebEnvironment determineWebEnvironment(ServletContext servletContext) {

        Class<? extends WebEnvironment> webEnvironmentClass = webEnvironmentClassFromServletContext(servletContext);
        WebEnvironment webEnvironment = null;

        // try service loader next
        if (webEnvironmentClass == null) {
            webEnvironment = webEnvironmentFromServiceLoader();
        }

        // if webEnvironment is not set, and ENVIRONMENT_CLASS_PARAM prop was not set, use the default
        if (webEnvironmentClass == null && webEnvironment == null) {
            webEnvironmentClass = getDefaultWebEnvironmentClass();
        }

        // at this point, we anything is set for the webEnvironmentClass, load it.
        if (webEnvironmentClass != null) {
            webEnvironment = (WebEnvironment) ClassUtils.newInstance(webEnvironmentClass);
        }

        return webEnvironment;
    }
    /**
     * Servlet Context config param for specifying the {@link WebEnvironment} implementation class to use:
     * {@code shiroEnvironmentClass}
     */
    public static final String ENVIRONMENT_CLASS_PARAM = "shiroEnvironmentClass";
    private Class<? extends WebEnvironment> webEnvironmentClassFromServletContext(ServletContext servletContext) {

        Class<? extends WebEnvironment> webEnvironmentClass = null;
        String className = servletContext.getInitParameter(ENVIRONMENT_CLASS_PARAM);
        ...
    }

在默认的org.apache.shiro.web.env.IniWebEnvironment 类中提供有返回当前shiro PathMatchingFilterChainResolver 对象的方法:DefaultWebEnvironment.getFilterChainResolver();

至此,所有问题解决。

转载于:https://my.oschina.net/yuyizyk/blog/1822740

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值