何本明細書中で使用されるように、次の、ユーザ認証と着陸権の検証を行うために、史郎、史郎初めて目に
Apacheの史郎は、認証、許可、パスワード、およびセッション管理のための強力で使いやすいJavaセキュリティフレームワークです。史郎のAPIの使用を理解しやすい、あなたが迅速かつ容易に、最小から最大のモバイルネットワークアプリケーションおよびエンタープライズアプリケーションに、任意のアプリケーションにアクセスすることができます。
主な機能
3つのコアコンポーネント:件名、セキュリティマネージャおよびレルム。
- 件名:つまり、「現在のユーザー。」しかし、史郎に、この概念の被写体が人物であるだけでなく、それは、サードパーティのプロセス、背景アカウント(デーモンのアカウント)または他の類似のものすることができます。それは単に「何かで現在のインタラクティブなソフトウェア」を意味します。
- 件名は、現在のユーザーのセキュリティ動作を表し、セキュリティマネージャは、セキュリティ操作のすべてのユーザーを管理します。
- セキュリティマネージャ:セキュリティマネージャによって内部コンポーネントのインスタンスを管理する四郎、典型的なファサードパターン、四郎のコアフレームワークであり、それを介してサービスのセキュリティ管理の多様性を提供します。
- レルム:史郎とアプリケーションのセキュリティデータ間の「ブリッジ」または「コネクター」としてレルム行為。つまり、ときにユーザーが行う認証(ログイン)および承認(アクセス制御)検証は、史郎は、アプリケーション構成レルム内のユーザーとその権限から情報を検索します。
史郎は、レルムを構築し、そのようなINIテキスト設定リソース、およびプロパティファイルに似LDAP、リレーショナルデータベース(JDBC)、など(ディレクトリ別名)セキュリティデータソースの大規模な数を、接続することができます。デフォルトのレルムは、需要を満たすことができない場合は、自分のレルム実装の代わりに、カスタムデータソースを挿入することができます。
史郎コアは、次の主要なカテゴリがあります。
- 認証、認証/ログイン、ユーザが適切なIDを持っていないことを確認してください。
- 認証、認可、確認するために、すなわち権限は、ユーザーが権限を認証したことを確認してください。
- 何の出口が存在しなくなるまで、それはセッション内のすべての情報ですが、ユーザーがログオンした後のセッションでセッションマネージャセッションマネージャ、。
- プロテクトデータセキュリティへの暗号化、暗号化
- ウェブサポートWebサポート、非常に簡単にウェブ環境に統合することができます。
- ユーザーが役割と、そのユーザーの情報、ログインするよう、キャッシュのキャッシュ/パーミッションが、これは効率を高めることができるたびにチェックする必要はありません。
- スレッド内のそのようなオープン別のスレッドを確認するための同時実行シロサポートマルチスレッドアプリケーション、権限が自動的に過去に増殖させることができます。
- テストは、テストのサポートを提供します。
- (彼らが許可されている場合)アイデンティティ別のユーザーへのアクセスを許可するふりをユーザーとして実行します。
- つまり、私は私を覚えている覚えて、これは非常に共通の特徴である最初のログイン後、次の時間が記録され戻ってきません。
Shiroeアーキテクチャは、おそらく主に、チャートによって史郎アーキテクチャを見ることができます:
- 件名:本体は、ユーザーがシステムにアクセスするためのプログラムで、対象となる場合がありすることができ、システムは、対象の認証、許可する必要があります。
- セキュリティマネージャ:認証および承認のためのセキュリティ管理体は、SecurityManagerを介して行われます。
- オーセンティケータ:オーセンティケータによる認証のための最終的な認証機関。
- 承認者:オーセンティケータによって最終的に認可する権限ボディ。
- SessionManagerの:ウェブアプリケーションのセッション管理セッションは通常、Webコンテナによって管理され、史郎は、管理対象のセッションを提供しています。
- sessionDao:によってsessionDao管理セッションデータ、
- CacheManager:キャッシュマネージャー主にセッションと認証データキャッシュ、などのCacheManagerによる認可データキャッシュ管理など、データ・キャッシュ管理のehcacheを統合。
- 分野:フィールドのデータは、レルムアクセス認証、許可データを通じて、ソースに対応します。
- 暗号化:パスワード管理コンポーネントは、暗号化/復号化のセットを提供し、開発を促進します。このような共通のハッシュ、暗号化/復号化やその他の機能を提供するなど。
次のように認証のフローチャート
次に、我々は、プロジェクトに直接設定しました
最初はポンポンジャーパッケージで導入され、自動的にMavenのダウンロードのshiro1.5は、あなた自身のローカルリポジトリにダウンロードすることができ、ダウンロード、または1.4.2に変更することはできません
<! - 第三方日志库- > < 依存> < groupIdを>コモンズ・ログ</ groupIdを> < たartifactId >コモンズ・ログ</ たartifactId > < バージョン> 1.2 </ バージョン> </ 依存関係> <!--Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序--> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> <!--shiro核心--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${org.apache.shiro.version}</version> </dependency> <!--shiro整合web--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${org.apache.shiro.version}</version> </dependency> <!--shiro应用缓存--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${org.apache.shiro.version}</version> </dependency> <!--整合spring-shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${org.apache.shiro.version}</version> </dependency>
首先需要在web.xml中配置过滤器,帮助我们拦截请求
<!--配置shiro过滤器,拦截所有请求--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <!--配置shiro的过滤路径--> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
接下来配置application-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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置realm数据源--> <bean id="employeeRealm" class="com.yang.web.realm.EmployeeRealm"> <property name="credentialsMatcher" ref="credentialsMatcher" /> </bean> <!--配置shiro过滤器--> <bean id="formFilter" class="com.yang.web.filter.FormFilter" /> <!--配置shiro安全管理器--> <bean id="securityManage" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="employeeRealm" /> <!--配置缓存--> <property name="cacheManager" ref="ehCache" /> </bean> <!--使用第三方去扫描shiro的注解--> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManage" /> </bean> <!--配置shiro过滤器--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 配置登陆认证的路径 如果没有配置该路径,对于没有认证过的请求,会跳转到login.jsp 如果配置了该路径: 如果请求是loginUrl的路径,那就会去做认证 其他请求,会去执行对应login的请求 --> <property name="loginUrl" value="/login" /> <!--重新配置表单监听的过滤器--> <property name="filters"> <map> <entry key="authc" value-ref="formFilter" /> </map> </property> <property name="securityManager" ref="securityManage"/> <!--配置shiro的过滤器pattern--> <property name="filterChainDefinitions"> <value> /static/** = anon <!--不需要进行登陆验证--> /login.jsp = anon <!--不需要进行登陆验证--> /logout = logout <!--配置注销接口--> /** = authc <!--除了上述请求外,都需要进行登陆验证--> </value> </property> </bean> <!-- 配置代理 true:使用cglib的方式 false:使用jdk接口动态代理 --> <aop:config proxy-target-class="true" /> <!--凭证匹配器--> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--散列算法--> <property name="hashAlgorithmName" value="md5" /> <!--散列算法--> <property name="hashIterations" value="2" /> </bean> <!--缓存管理器--> <bean id="ehCache" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" /> </bean> </beans>
需要在springMvc的配置文件中导入这个包
<!--导入shiro的配置--> <import resource="classpath:application-shiro.xml" />
我们在配置shiro是,配置了缓存管理器,需要创建配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
我们在shiro中配置了loginUrl,因此需要在控制层实现对应的url
package com.yang.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * 配置了loginUrl,需要设置对应的函数 */ @Controller public class LoginController { @RequestMapping("/login") public String login() { return "redirect:login.jsp"; } }
接下来需要配置验证以及授权的realm
package com.yang.web.realm; import com.yang.domain.Employee; import com.yang.service.EmployeeService; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; /** * 设置员工权限realm,需要继承authorizingRealm */ public class EmployeeRealm extends AuthorizingRealm { /*注入*/ @Autowired private EmployeeService employeeService; /*认证*/ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("认证,来啦"); // 获取身份信息 String username = (String) token.getPrincipal(); System.out.println(username); // 根据当前用户名查看当前是否存在当前用户 Employee employee = employeeService.getEmployeeByName(username); // 如果没有查询到employee、,就返回为空 if (employee == null) { return null; } // 进行认证 // 认证的参数,主体,正确的密码,盐,还有当前realm的名称 return new SimpleAuthenticationInfo(employee, employee.getPassword(), ByteSource.Util.bytes("!@#QAZwsx"), this.getName()); } /*授权*/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 获取客户主体 Employee employee = (Employee) principalCollection.getPrimaryPrincipal(); // 声明角色集合 List<String> roles = null; // 声明权限集合 List<String> permissions = null; // 判断该角色是否是管理员 if (employee.getAdmin()) { // 是管理员,增加所有权限 permissions = new ArrayList<>(); roles = new ArrayList<>(); // 获取所有权限 permissions.add("*:*"); } else { // 查询该员工所拥有的角色集合 roles = employeeService.getRoleByEmployeeId(employee.getId()); // 查询该员工所有的权限集合 permissions = employeeService.getPermissionByEmployeeId(employee.getId()); } // 添加授权信息 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(roles); info.addStringPermissions(permissions); return info; } }
为了获取验证结果,我们需要配置form表单过滤器
package com.yang.web.filter; import com.fasterxml.jackson.databind.ObjectMapper; import com.yang.domain.AjaxRes; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; /** * 这个等登陆成功会调用,为了通知web,在这里面重写方法,重写表单监听的过滤器成功以及失败的方法 */ public class FormFilter extends FormAuthenticationFilter { /*当认证成功的时候,会进行调用*/ protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { System.out.println(subject); // 因为回传信息包含中文,先设置字节码 response.setCharacterEncoding("utf-8"); AjaxRes ajaxRes = new AjaxRes(); ajaxRes.setSuccess(true); ajaxRes.setMsg("登陆成功!"); // 把对象转化为json字符串 String resString = new ObjectMapper().writeValueAsString(ajaxRes); // 将字符串写入响应对象 response.getWriter().print(resString); // return false 才会往下走,否则就组织了 return false; } /*当认证失败的时候,就会调用,e就是抛出异常*/ protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { // 设置响应数据 AjaxRes ajaxRes = new AjaxRes(); ajaxRes.setSuccess(false); if (e != null) { // 获取异常类的名称 String name = e.getClass().getName(); // 没有账号 if (name.equals(UnknownAccountException.class.getName())) { ajaxRes.setMsg("账号不正确"); } else if (name.equals(IncorrectCredentialsException.class.getName())) { ajaxRes.setMsg("密码不正确"); // 密码有误异常 } else { ajaxRes.setMsg("不确认错误"); // 其他错误 } } // 序列化返回结果并且放置到响应对象中 try { String resString = new ObjectMapper().writeValueAsString(ajaxRes); response.setCharacterEncoding("utf-8"); response.getWriter().print(resString); } catch (IOException ex) { ex.printStackTrace(); } System.out.println(ajaxRes.getMsg()); // return false 才会往下走,否则就阻止了 // return true; // 默认是返回true return false; } }
同时对于授权的使用,前端
<shiro:hasPermission name="employee:add"> <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-add',plain:true" id="add">添加</a> </shiro:hasPermission>
后端
/*编辑员工*/ @RequestMapping("/employee/update") @ResponseBody @RequiresPermissions("employee:update") // 配置权限 public AjaxRes employeeUpdate(Employee employee){ return employeeService.updateEmployee(employee); }
为了处理异常,比如没有权限。一般在该类下写一个方法捕捉异常并处理
/* 设置异常处理 参数method就是发生异常的方法 */ @ExceptionHandler(AuthorizationException.class) public void handleShiroException(HandlerMethod method, HttpServletResponse response)throws Exception{ /*如果授权异常,则跳转授权页面*/ // 获取异常方法中的是否是json请求 ResponseBody methodAnnotation = method.getMethodAnnotation(ResponseBody.class); if(methodAnnotation != null){ // 这个就是ajax的请求 AjaxRes ajaxRes = new AjaxRes(); ajaxRes.setSuccess(false); ajaxRes.setMsg("没有权限操作"); String res = new ObjectMapper().writeValueAsString(ajaxRes); response.setCharacterEncoding("utf-8"); response.getWriter().print(res); }else{ response.sendRedirect("error-permission.jsp"); } }