前書き:
Shiroはapacheのオープンソースセキュリティフレームワークであり、セキュリティ認証ソフトウェアシステムに関連する機能を引き出して、ユーザー認証、権限、承認、暗号化、セッション管理などの機能を実現し、共通のセキュリティ認証フレームワークを形成し、 shiroを使用して開発を完了します認証、承認、その他の機能を非常に迅速に実行し、システムコストを削減します。
要約構造:
詳細な構造:
最初のレイヤー:件名
2番目のレイヤー:securiryManager- 管理コンポーネントの作業を調整するために使用されるShiroのコア
オーセンティケーター:認証マネージャー- 認証操作の実行を担当します
承認者:承認マネージャー- 承認の検出を担当します
セッション管理:
3番目のレイヤー:レルム-レルムは、shiroとアプリケーションのセキュリティデータの間の架け橋です。
実際の戦闘:
配置Shiro安全过滤器
WEB-INF / web.xml
<!-- 配置Shiro安全过滤器 --> <filter> <filter-name>DelegatingFilterProxy</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 初始化参数 --> <init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>DelegatingFilterProxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
spring-configs.xml
<!-shiroフレームワークを構成します->
<!-レルムオブジェクトを構成します(春に管理されます)->
<bean id = "userRealm"
class = "com.jt.sys.service.realm.ShiroUserRealm">
<!-資格情報マッチャー(パスワード暗号化)->
<property name = "credentialsMatcher">
<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name = "hashAlgorithmName" value = "MD5 "/>
<!-<property name =" hashIterations "value =" 1024 "/>->
</ bean>
</ property>
</ bean>
<!-CacheManagerオブジェクトを構成します(不要、主にTo性能を上げる、認証情報と承認情報はキャッシュできます)->
<bean id = "cacheManager" class = "org.apache.shiro.cache.ehcache.EhCacheManager">
<!-net.sf.ehcache.CacheManagerインスタンスが既にある場合は、ここに設定します。そうでない場合は、
デフォルトの構成で新しいものが作成されます:
<property name = "cacheManager" ref = "ehCacheManager" />->
<!-ビルド済みのnet.sf.ehcacheがない場合.CacheManagerインスタンスを挿入しますが
、特定のEhcache構成を使用する場合は、ここで指定します。そうしない場合、デフォルトは
> - :使用されます
。<プロパティ名= "cacheManagerConfigFile"値= "クラスパス:ehcache.xml" />
</豆>
!<を-配置のSecurityManager对象(此对象时史郎框架核心)->
<bean id = "
class = "org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name = "realm" ref = "userRealm" />
<property name = "cacheManager" ref = "cacheManager" />
</ bean>
<! -構成shiroFilter(認証される、解放を要求するリソース要求をフィルタリングするためのこのフィルター配置によって実現されます) ->
<Bean ID = "shiroFilter" class = "org.apache.shiro.spring.web.ShiroFilterFactoryBean ">
<!-shiroのコアセキュリティインターフェイス->
<property name =" securityManager "ref =" securityManager "/>
<!-ログイン時の接続が必要です->
<property name =" loginUrl "value = "/loginUI.do"> </ property>
<!-ログインが成功した後にジャンプする接続(これはログインで処理されています)->
<!-<property name = "successUrl" value = "/ index.jsp"> </ property>- >>
<!-許可されていないリソースにアクセスする場合、ジャンプする接続
<property name = "unauthorizedUrl" value = "/ default.html"> </ property>->
<!-shiro接続制約構成->
<property name = "filterChainDefinitions">
<value>
<!-静的リソース設定への匿名アクセスを許可する->
/ bower_components / ** = anon
/ build / ** = anon
/ dist / ** = anon
/ plugins / ** = anon
/doLogin.do = anon
<!-終了->
/doLogout.do = logout <!-サブジェクトのログアウトメソッドが呼び出され、このメソッドはセッションをクリアします->
<! -残りの他のパスは、アクセスする前に認証する必要があります->
/ ** = authc
</ value>
</ property>
</ bean>
<!-Shiro生命フレームワーク理器->
<bean id = "lifecycleBeanPostProcessor"
class = "org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-启用shiro注解権利限检查(@RequestPermissions)->
<bean class = "org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on = "lifecycleBeanPostProcessor" />
<bean class = "org.apache.shiro.spring .security.interceptor.AuthorizationAttributeSourceAdvisor ">
<property name =" securityManager "
ref =" securityManager "/>
</ bean>
認証:
認証プロセス:
- システムはサブジェクトのログインメソッドを呼び出して、ユーザー情報トークンをSecurityManagerに送信します
subject.login(トークン)
//このリクエストはSecurityManagerに送信されます
// SecurityManagerは認証プロセッサAuthenticatorを呼び出します
//認証プロセッサは、関連するRealmオブジェクトにアクセスして、認証情報を取得します
- SecurityManagerは、認証操作をオーセンティケーターオブジェクトAuthenticatorに委任します
- オーセンティケーターはID情報をレルムに渡します。
SysUser sysUser = sysUserDao.findUserByUserName(username);
- レルムはデータベースにアクセスしてユーザー情報を取得し、情報をカプセル化して返します。
//3.2ユーザー情報をカプセル化する
AuthenticationInfo情報
= new SimpleAuthenticationInfo(
sysUser.getUsername()、//マスターID
sysUser.getPassword()、//暗号化されたパスワード
byteSource、// saltに対応するバイトソースオブジェクト
getName()); //レルムの名前
5)オーセンティケーターは、レルムから返された情報を認証します。
SysLoginController.java
@Controller @RequestMapping("/") public class SysLoginController { @Autowired private SysUserService sysUserService; @RequestMapping("loginUI") public String loginUI(){ return "login"; } @RequestMapping("doLogin") @ResponseBody public JsonResult doLogin(String username, String password){ String r=DigestUtils.md5DigestAsHex("123456".getBytes()); System.out.println("r="+r); sysUserService.login(username, password); return new JsonResult("login ok"); } }
@Service public class SysUserServiceImpl implements SysUserService { @Autowired private SysUserDao sysUserDao; @Autowired private SysRoleDao sysRoleDao; @Autowired private SysUserRoleDao sysUserRoleDao; @Override public void login(String username,String password) { System.out.println("service.login"); //0.参数合法性验证 if(StringUtils.isEmpty(username)) throw new ServiceException("用户名不能为空"); if(StringUtils.isEmpty(password)) throw new ServiceException("密码不能为空"); //1.获取Subject(主体)对象 Subject subject=SecurityUtils.getSubject(); //2.封装用户名和密码 UsernamePasswordToken token=new UsernamePasswordToken(username, password); //3.执行身份认证 try { subject.login(token); //此请求会提交给SecurityManager //SecurityManager会调用认证处理器Authenticator //认证处理器会去访问相关Realm对象获取认证信息 } catch (AuthenticationException e) { e.printStackTrace(); throw new ServiceException("用户名或密码不正确"); } //4.记录用户信息 Session session= SecurityUtils.getSubject().getSession(); session.setAttribute("user", username); }
AuthorizingRealmを継承し、doGetAuthenticationInfoをオーバーライドします
Realmを介して基本認証と権限制御を実現します
public class ShiroUserRealm extends AuthorizingRealm { @Autowired private SysUserDao sysUserDao; /** * 完成认证信息的获取以及封装 * 此方法何时调用?(执行登陆认证时调用) * @param * 用于接收用户身份以及凭证信息的对象(用户输入的) * * @return AuthenticationInfo * 封装了认证信息的对象(从数据库查询到的) * * client-->controller-->service-->realm */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("realm.doGetAuthenticationInfo"); //1.获取用户身份信息 UsernamePasswordToken uToken= (UsernamePasswordToken)token; String username=uToken.getUsername(); //2.基于用户身份查询数据库信息 SysUser sysUser= sysUserDao.findUserByUserName(username); //3.对查询结果进行封装. //3.1获取用户salt值,并将其转换为一个字节源对象 ByteSource byteSource= ByteSource.Util.bytes(sysUser.getSalt()); //3.2对用户信息进行封装返回. AuthenticationInfo info= new SimpleAuthenticationInfo( sysUser.getUsername(), //主身份 sysUser.getPassword(), //已加密的密码 byteSource,//salt对应的字节源对象 getName());//realm 的名字 return info; } }
public interface SysUserDao { /** * 根据用户名查找用户信息 * @param username * @return */ SysUser findUserByUserName(String username); }
<select id="findUserByUserName" resultType="sysUser"> select * from sys_users where username=#{username} </select>
承認の実装:
処理する:
- システムは、サブジェクト関連のメソッドisPermittedを呼び出して、リソース/ hasRoleに基づいてユーザー情報をSecurityManagerに送信します。
2)SecurityManagerは、権限検出操作をAuthorizerオブジェクトに委任します
3)承認者はユーザー情報をレルムに委任します。
List <String> list = sysUserDao.findUserPermissions(username);
- レルムはデータベースにアクセスして、ユーザー権限情報を取得し、それをカプセル化します。
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
- 承認者は、ユーザー承認情報を判断します。
@RequiresPermissions( "sys:user:update")
@RequestMapping("doDeleteObject") @ResponseBody public JsonResult doDeleteObject(String idStr){ sysRoleService.deleteObject(idStr); return new JsonResult(); }
@RequiresPermissions("sys:role:delete") @Override public int deleteObject(String idStr) { //1.参数合法性验证 if(StringUtils.isEmpty(idStr)) throw new ServiceException("必须选中才能删除"); //2.解析字符串 String[] ids=idStr.split(","); //3.调用数据层方法执行删除操作 int rows=sysRoleDao.deleteObject(ids); for(String id:ids){ sysRoleMenuDao.deleteObject(Integer.valueOf(id)); sysUserRoleDao.deleteObject(null, Integer.valueOf(id)); } //4.返回处理结果 if(rows==0) throw new ServiceException("数据已经不存在"); return rows; }
Realmを介して基本認証と権限制御を実現します
public class ShiroUserRealm extends AuthorizingRealm { @Autowired private SysUserDao sysUserDao; /*** * 完成授权信息的获取以及封装. * 此方法何时调用?(执行授权检测时调用) * */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { System.out.println("realm.doGetAuthorizationInfo"); //1.获取登陆用户身份信息 String username= (String)principals.getPrimaryPrincipal(); //2.查找用户的权限信息 List<String> list=//sys:user:update,sys:user:view,...., sysUserDao.findUserPermissions(username); System.out.println("list="+list); Set<String> permissions=new HashSet<>(); for(String permission:list){ if(!StringUtils.isEmpty(permission)){ permissions.add(permission); } } System.out.println("set="+permissions); //3.对权限信息进行封装 SimpleAuthorizationInfo info= new SimpleAuthorizationInfo(); info.setStringPermissions(permissions); return info; }
/** * 根据用户id查找用户权限标识信息 * 例如:sys:role:view,sys:role:add * @param userId * @return */ List<String> findUserPermissions(String username);
<select id="findUserPermissions" resultType="string"> select m.permission from sys_users u join sys_user_roles ur join sys_role_menus rm join sys_menus m on u.id=ur.user_id and ur.role_id=rm.role_id and rm.menu_id=m.id where u.username=#{username} </select>
<!-- 整合Shiro 安全框架--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
関連表:
sys_roles
sys_users
sys_menus
sys_role_menus
sys_user_roles
CREATE TABLE `sys_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL COMMENT '角色名称',
`note` varchar(500) DEFAULT NULL COMMENT '备注',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COMMENT='角色';CREATE TABLE `sys_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(50) DEFAULT NULL COMMENT '盐 密码加密时前缀,使加密后的值不同',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
`valid` tinyint(4) DEFAULT NULL COMMENT '状态 0:禁用 1:正常 默认值 :1',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='系统用户';CREATE TABLE `sys_menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '资源名称',
`url` varchar(200) DEFAULT NULL COMMENT '资源URL',
`type` int(11) DEFAULT NULL COMMENT '类型 1:菜单 2:按钮',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
`parentId` int(11) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`permission` varchar(500) DEFAULT NULL COMMENT '授权(如:user:create)',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=141 DEFAULT CHARSET=utf8 COMMENT='资源管理';CREATE TABLE `sys_role_menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
`menu_id` int(11) DEFAULT NULL COMMENT 'ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1218 DEFAULT CHARSET=utf8 COMMENT='角色与菜单对应关系';CREATE TABLE `sys_user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';INSERT INTO `jt_sys`.`sys_users`(`id`, `username`, `password`, `salt`, `email`, `mobile`, `valid`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (1, 'admin', '4ebd394fbd25e495e0753a7dc9889a8e', '7adb778c-e7d3-4dd3-a3c5-5f80a158006d', '[email protected]', '13624356789', 1, NULL, '2018-01-13 02:06:45', NULL, 'admin');
INSERT INTO `jt_sys`.`sys_menus`(`id`, `name`, `url`, `type`, `sort`, `note`, `parentId`, `permission`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (45, '用户管理', 'user/listUI.do', 1, 45, NULL, 8, 'sys:user:view', '2017-07-12 15:15:59', '2017-07-21 17:36:01', 'admin', 'admin');