Широ [фильтр авторизации, интеграция с ehcache, проверочный код, запомнить меня]

задняя часть база данных Shiro Realm

предисловие

Основные моменты знаний, объясненные в этой статье, следующие:

  • Использование фильтра авторизации Широ
  • Тайник Широ
    • Интеграция с Ehcache
  • Приложение Shiro -> реализовать функцию кода подтверждения
  • функция запомни меня

1. Проверка фильтра авторизации

Наш фильтр авторизации использует для перехвата разрешенияAuthorizationFilter. Мы можем настроить правила фильтрации в application-shiro


		<!--商品查询需要商品查询权限 -->
		/items/queryItems.action = perms[item:query]
		/items/editItems.action = perms[item:edit] 

Процесс тестирования: 1,Настройте правила фильтрации в applicationContext-shiro.xml.

  • <!--商品查询需要商品查询权限 -->
  • /items/queryItems.action = perms[item:query]

2,После аутентификации пользователя он запрашивает /items/queryItems.action3. Перехвачено PermissionsAuthorizationFilter,Обнаружено, что требуется разрешение «item:query».4. РазрешенияАвторизацияФильтрВызовите doGetAuthorizationInfo в области, чтобы получить правильные разрешения в базе данных.5. Фильтр PermissionsAuthorizationFilter сравнивает элемент:запрос и разрешение, полученное от области.Если «элемент:запрос» находится в списке разрешений, возвращаемом областью, авторизация проходит.

Получите информацию об аутентификации из области, запросите соответствующие разрешения пользователя и инкапсулируйте их в simpleAuthorizationInfo, а PermissionsAuthorizationFilter сравнит их в соответствии с соответствующими разрешениями.


@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		
		//从 principals获取主身份信息
		//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
		ActiveUser activeUser =  (ActiveUser) principals.getPrimaryPrincipal();
		
		//根据身份信息获取权限信息
		//从数据库获取到权限数据
		List<SysPermission> permissionList = null;
		try {
			permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//单独定一个集合对象 
		List<String> permissions = new ArrayList<String>();
		if(permissionList!=null){
			for(SysPermission sysPermission:permissionList){
				//将数据库中的权限标签 符放入集合
				permissions.add(sysPermission.getPercode());
			}
		}
		
		
	/*	List<String> permissions = new ArrayList<String>();
		permissions.add("user:create");//用户的创建
		permissions.add("item:query");//商品查询权限
		permissions.add("item:add");//商品添加权限
		permissions.add("item:edit");//商品修改权限
*/		//....
		
		//查到权限数据,返回授权信息(要包括 上边的permissions)
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
		simpleAuthorizationInfo.addStringPermissions(permissions);

		return simpleAuthorizationInfo;
	}

В бине мы настроили: если нет разрешения, на какую JSP-страницу переходить


		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
		<property name="unauthorizedUrl" value="/refuse.jsp" />

Пока проблема снова возникает:

1,Чтобы настроить ссылку фильтра в applicationContext-shiro.xml, вам нужно настроить все URL-адреса и разрешения соответственно, что довольно скучно и неудобно в использовании.

2,Каждая авторизация должна вызывать область для запроса к базе данных, что оказывает большое влияние на производительность системы и может быть решено с помощью кэша Широ.


Во-вторых, используйте авторизацию конфигурации аннотаций и этикеток.

Приведенный выше метод по-прежнему требует от нас соответствующей настройки всех URL-адресов и разрешений, что относительно неудобно. Есть два других способа авторизации

  • Аннотированный
  • Вкладки

2.1 Аннотация

Если вы хотите использовать аннотации, вы должны использовать SpringВключить поддержку aop класса контроллера


	<!-- 开启aop,对类代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	<!-- 开启shiro注解支持 -->
	<bean
		class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

Просто используйте аннотации в контроллере для его настройки, вам не нужно настраивать все это в нашем приложении-широ


	//商品信息方法
	@RequestMapping("/queryItems")
	@RequiresPermissions("item:query")//执行queryItems需要"item:query"权限
	public ModelAndView queryItems(HttpServletRequest request) throws Exception {
		
		System.out.println(request.getParameter("id"));
	
		//调用service查询商品列表
		List<ItemsCustom> itemsList = itemsService.findItemsList(null);

		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("itemsList", itemsList);
		// 指定逻辑视图名
		modelAndView.setViewName("itemsList");

		return modelAndView;
	}

2.2 авторизация тега jsp

这里写图片描述

这里写图片描述

При вызове метода контроллера, поскольку @RequiresPermissions("item:query") добавляется к методу, shiro вызывает область, чтобы получить информацию о разрешении в базе данных, чтобы увидеть, существует ли "item:query" в данных разрешения, если нет, будет отклонен доступ, если он существует, авторизуйте его.

При отображении jsp-страницы, если на странице встречается , shiro вызывает область, чтобы получить информацию о разрешении в базе данных, чтобы узнать, существует ли item:update в данных разрешения. , авторизуйтесь, если он существует.


3. Тайник Широ

Для вышеуказанной авторизации для частых запросов к базе данных вам необходимо использовать широ кеш

3.1 Процесс кэширования

Кэш для информации об аутентификации и информации об авторизации предоставляется в shiro. По умолчанию shiro отключает кэширование информации для аутентификации.Кэш для авторизационной информации включен по умолчанию в Широ. Основной кэш информации об авторизации исследования, т.к.Объем авторизованных данных велик.

Аутентификация пользователя пройдена.

Пользователь авторизован в первый раз: вызовите область, чтобы запросить базу данных Пользователь авторизуется второй раз: без вызова области для запроса базы данных информация об авторизации (идентификатор авторизации) извлекается напрямую из кеша.

3.2 Использование ehcache и интеграции Shiro

импортный пакет jar

这里写图片描述

Настройте диспетчер кеша и внедрите его в диспетчер безопасности.


<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    	<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>


	<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm" />
		<!-- 注入缓存管理器 -->
		<property name="cacheManager" ref="cacheManager"/>
	</bean>

конфигурационный файл ehcacheshiro-ehcache.xml:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<!--diskStore:缓存数据持久化的目录 地址  -->
	<diskStore path="F:\develop\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		diskPersistent="false"
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

3.3 Очистка кэша

Если пользователь выходит нормально, кэш автоматически очищается. Если пользователь выходит из системы аварийно, кэш автоматически очищается.

Есть другая ситуация:

  • Когда администратор изменяет разрешения пользователя, но пользователь не вышел из системы, по умолчанию измененные разрешения не могут вступить в силу немедленно. Требуется ручное программирование:Вызовите метод clearCache области, чтобы очистить кеш после изменения разрешения.

очистить кэш:


	//清除缓存
	public void clearCached() {
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principals);
	}

3.4sessionManager

После интеграции с Широ,Используя управление сеансом shiro, shiro предоставляет sessionDao для управления данными сеанса.

Настроить диспетчер сеансов


<!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- session的失效时长,单位毫秒 -->
        <property name="globalSessionTimeout" value="600000"/>
        <!-- 删除失效的session -->
        <property name="deleteInvalidSessions" value="true"/>
    </bean>

Внедрить в диспетчер безопасности


<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm" />
		<!-- 注入缓存管理器 -->
		<property name="cacheManager" ref="cacheManager"/>
		<!-- 注入session管理器 -->
		<property name="sessionManager" ref="sessionManager" />
	
</bean>

4. Код подтверждения

При входе в систему мы обычно устанавливаем код подтверждения, но если мы используем Широ, то Широ по умолчанию использует FormAuthenticationFilter для аутентификации формы.

И наша функция проверки проверки должнаДобавьте в FormAuthenticationFilter, чтобы проверить код подтверждения перед аутентификацией..

FormAuthenticationFilter — это функция Широ по умолчанию, мы хотим проверить код подтверждения перед FormAuthenticationFilter, простоНеобходимо наследовать класс FormAuthenticationFilter и переписать его метод аутентификации.!

4.1 Класс аутентификации пользовательской формы


public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

	//原FormAuthenticationFilter的认证方法
	@Override
	protected boolean onAccessDenied(ServletRequest request,
			ServletResponse response) throws Exception {
		//在这里进行验证码的校验
		
		//从session获取正确验证码
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpSession session =httpServletRequest.getSession();
		//取出session的验证码(正确的验证码)
		String validateCode = (String) session.getAttribute("validateCode");
		
		//取出页面的验证码
		//输入的验证和session中的验证进行对比 
		String randomcode = httpServletRequest.getParameter("randomcode");
		if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
			//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
			httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
			//拒绝访问,不再校验账号和密码 
			return true; 
		}
		return super.onAccessDenied(request, response);
	}

		
}

4.2 Настройка пользовательских классов

После того, как мы напишем наш собственный класс, нам нужно настроить наш собственный класс в файле конфигурации Shiro.

Так как это наш обычай,Нам не нужно имя пользователя для использования имени пользователя и пароль для использования пароля, который мы можем настроить.


<!-- 自定义form认证过虑器 -->
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
	<bean id="formAuthenticationFilter" 
	class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
		<!-- 表单中账号的input名称 -->
		<property name="usernameParam" value="username" />
		<!-- 表单中密码的input名称 -->
		<property name="passwordParam" value="password" />
 </bean>

Внедрить пользовательские фильтры в бобы Широ



		<!-- 自定义filter配置 -->
		<property name="filters">
			<map>
				<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property>
		

Добавьте ненормальное суждение об ошибке кода проверки в нашем контроллере.От нашего контроллера мы можем узнать, почему мы должны хранить информацию об ошибке в объекте домена запроса shiroLoginFailure, потому что мы должны получить информацию в контроллере, чтобы дать пользователю соответствующие подсказки



	@RequestMapping("login")
	public String login(HttpServletRequest request)throws Exception{
		
		//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
		//根据shiro返回的异常类路径判断,抛出指定异常信息
		if(exceptionClassName!=null){
			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
				//最终会抛给异常处理器
				throw new CustomException("账号不存在");
			} else if (IncorrectCredentialsException.class.getName().equals(
					exceptionClassName)) {
				throw new CustomException("用户名/密码错误");
			} else if("randomCodeError".equals(exceptionClassName)){
				throw new CustomException("验证码错误 ");
			}else {
				throw new Exception();//最终在异常处理器生成未知错误
			}
		}
		//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
		//登陆失败还到login页面
		return "login";
	}

这里写图片描述


	<TR>
		<TD>验证码:</TD>
		<TD><input id="randomcode" name="randomcode" size="8" /> <img
				id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
				width="56" height="20" align='absMiddle' /> <a
				href=javascript:randomcode_refresh()>刷新</a></TD>
	</TR>

5. Запомни меня

Shiro также предоставляет возможность запоминать имена пользователей и пароли.!

Когда пользователь входит в систему и выбирает «автоматический вход», идентификационная информация будет записана в файл cookie, если вход будет успешным, и идентификационная информация будет извлечена из файла cookie для следующего входа в систему для реализации автоматического входа.

Чтобы реализовать эту функцию, наша аутентификационная информация должна бытьРеализовать сериализуемый интерфейс.



public class ActiveUser implements java.io.Serializable {
	private String userid;//用户id(主键)
	private String usercode;// 用户账号
	private String username;// 用户名称

	private List<SysPermission> menus;// 菜单
	private List<SysPermission> permissions;// 权限


}

5.1 Настройка менеджера памяти


<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cookie" ref="rememberMeCookie" />
	</bean>
	<!-- 记住我cookie -->
	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<!-- rememberMe是cookie的名字 -->
		<constructor-arg value="rememberMe" />
		<!-- 记住我cookie生效时间30天 -->
		<property name="maxAge" value="2592000" />
	</bean>


Внедрить в класс диспетчера безопасности


	<!-- securityManager安全管理器 -->
	<bean id="securityManager"~~~····
		<property name="cacheManager" ref="cacheManager"/>
		<!-- 注入session管理器 -->
		<property name="sessionManager" ref="sessionManager" />
		<!-- 记住我 -->
		<property name="rememberMeManager" ref="rememberMeManager"/>	
	</bean>



Настройте входное имя страницы:


			<tr>
				<TD></TD>
				<td><input type="checkbox" name="rememberMe" />自动登陆</td>
			</tr>

Если установлено «запомнить меня», то при посещении определенных URL нам не нужно авторизоваться.Запомнит конфигурацию адреса, к которой я могу получить доступ, и позволит перехватить UserFilter.

		<!-- 配置记住我或认证通过可以访问的地址 -->
		/index.jsp  = user
		/first.action = user
		/welcome.jsp = user

6. Резюме

  • Процесс авторизации Широ аналогичен процессу аутентификации.Путь, требующий авторизации, настраивается в конфигурационном файле.При доступе к пути Широ фильтрует, чтобы найти reaml, а reaml возвращает данные для сравнения.
  • Shiro поддерживает аннотированную авторизацию, используя аннотации непосредственно в методе Controller, чтобы объявить, что доступ к этому методу требует авторизации.
  • Shiro также поддерживает авторизацию лейблов, но в целом редко используется.
  • Поскольку к базе данных необходимо каждый раз запрашивать reaml, производительность будет низкой. Shiro по умолчанию поддерживает кэширование авторизации. Для достижения хороших результатов мы используем Ehcache для управления кешем Широ.
  • Настройте диспетчер сеансов для контроля времени сеанса
  • Очистить кеш вручную
  • Поскольку код подтверждения обычно требуется перед проверкой имени пользователя и пароля. Поэтому нам нужно переписать функцию проверки формы, сначала пусть она проверяет неверный ли проверочный код, если проверочный код неверный, то логин и пароль не нужно проверять.
  • Настройте пользовательский класс проверки формы.
  • Используя функцию «Запомнить меня», предоставляемую Широ, если пользователь уже аутентифицирован, нет необходимости снова входить в систему. Некоторые страницы доступны напрямую.

Если в статье есть ошибки, пожалуйста, исправьте меня и поделитесь друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y