1. shiro 权限管理在J2EE企业级开发中的应用与实战| 程序员的进阶平台www.itzixi.com讲师:Chacky 2. | 程序员的进阶平台www.itzixi.com1、了解基于角色/资源的权限管理方式
2、掌握权限数据模型,数据库表结构
3、了解基于url拦截的权限管理
4、shiro实现用户登录(认证)
5、shiro实现用户权限(授权)
6、J2EE中shiro与web项目的整合,主要是结合spring
7、项目实战:整合到LeeCX开源项目中,实现基于角色以及资源的授权模块一、课程目标 3. | 程序员的进阶平台www.itzixi.com 一般来说,只要有用户参与,那么该系统都会需要权限管理,权限管理实现了对用户访问系统 指定功能的限制,按照管理员定义的安全规则或权限策略,限制用户只能访问自己被授权的那些资源路径。
权限管理包括用户认证和授权两部分(俗称登录和鉴权)。也就是说先要进行用户的登录,登录以后会对用户访问的功能模块(即:访问资源的url路径)进行权限验证。二、权限管理的介绍与原理
2.1 什么是权限管理 4. | 程序员的进阶平台www.itzixi.com2.2 用户认证(用户登录) 身份认证,简单来说就是登录。检验一个用户是否为合法用户的业务处理过程。最常见的方式为系统对用户输入的用户名和密码判断是否匹配数据库中的记录。
除此之外还有指纹认证,一卡通认证,脸部扫描,这些都需要硬件设备,比如指纹采集仪,pose机,刷脸(脸部识别系统/iphoneX)等。 5. | 程序员的进阶平台www.itzixi.com2.3 用户认证流程图流程中的出现主要对象:
Subject:主体,大部分情况下就是用户,但也可以是api接口(rest服务,手机端访问等),去访问系统的功能,系统需要对Subject进行身份认证。
Principal:身份信息,一般来说是唯一的,一个主体可以有多个身份,但是都有一个主身份信息(primary principal)。主要体现在账户系统和子账户。
Credential:凭证token,可以是密码 、证书、指纹、人脸等。
总结一句话:主体用户在进行身份认证时需要提供身份信息和凭证信息。简单来说就是登录。 6. | 程序员的进阶平台www.itzixi.com2.4 用户授权(验证用户是否拥有访问的权限) 授权,即访问控制,控制用户能否访问哪些资源。在用户进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的 7. | 程序员的进阶平台www.itzixi.com2.5 用户授权流程图流程中的出现主要对象:
Subject:主体,此时的主体已经经过登录之后认证过了的。
Resource:主体用户所访问的资源或者功能。比如用户要删除一个员工记录,这个员工记录在数据库中的主键pid为1001,那么这个资源就是1001这条在数据库中所对应的记录。在java中就是一个对象。或者查询所有员工的页面,这些员工所在数据库中的数据集也是这个资源resource。
Permission:凭证token,可以是密码 、证书、指纹、人脸等。
总结一句话:[主体用户] 对 [资源] 进行 [增删改查] 的操作 8. | 程序员的进阶平台www.itzixi.com2.6 顶级账户分配权限用户需要被分配相应的权限才可访问相应的资源。权限是对于资源的操作一张许可证。
给用户分配资源权限需要将权限的相关信息保存到数据库。
这些相关内容包含:用户信息、权限管理、用户分配的权限信息(数据模型) 9. | 程序员的进阶平台www.itzixi.com2.6.1 权限模型设计主体(user、password)
权限(权限名称、资源id)/资源(资源名称、访问url)
角色(角色名称)
角色和权限关系(角色id、权限id)
主体和角色关系(主体id、角色id) 10. | 程序员的进阶平台www.itzixi.com2.7 权限分配有两种2.7.1 基于角色的权限控制
这些角色包含比如:总经理,技术总监,运营总监
不同的角色可以访问到的数据权限不同
例:
if (user.ifHasRole('总经理')) {
// 访问所有资源
}
if (user.ifHasRole('技术总监')) {
// 访问数据监控
// 访问服务器信息
}
if (user.ifHasRole('运营总监')) {
// 访问用户数据
// 访问报表数据
}
缺点:在经常变更角色权限的管理系统中无法做到动态管理,因为用户主体在整个系统中都是活动对象。
举个栗子:
技术总监需要查看用户数据,而用户数据只有运营总监才有权限看,那么此时需要修改代码了:
if (user.ifHasRole('技术总监') || user.ifHasRole('运营总监')) {
// 访问数据监控
// 访问服务器信息
}
所以说这样的方式不利于系统的扩展,即可扩展性太弱。
当然如果系统可以让人身兼多只,那么多选角色即可,这个用户同时有技术总监以及运营总监的身份。
但是如果涉及到HR总监甚至实施总监的功能,那么这个用户就同时有4个角色,这样真的好吗?
11. | 程序员的进阶平台www.itzixi.com2.7.2 基于资源的权限控制
资源即功能,也就是当前系统所包含的所有功能模块,这些是不变的
比如button,link,menu等
此时访问这些资源需要permission来鉴权
例:
if (user.ifHasPermission('访问用户数据')) {
// 访问用户数据
}
if (user.ifHasPermission('访问报表数据')) {
// 访问报表数据
}
if (user.ifHasPermission('访问用户数据') || user.ifHasPermission('访问报表数据')) {
// 访问用户数据 和 报表数据
}
那么这样的话我们只有针对当前登录的用户,增加相应的资源权限即可,没有必要再去维护代码,减少了程序的代码维护。因为资源在整个系统中是静态对象。
*增减资源权限需要另开一个权限资源分配的模块,在这个模块中设置用户的资源权限即可,一般由顶级账户来操作。 12. | 程序员的进阶平台www.itzixi.com3.1 什么是粗/细粒度的权限
粗粒度权限,是指对现有资源类型的权限管理。资源类型可以有:url、button、link、menu
粗粒度的权限分配:总经理可以分配到用户访问的页面,比如用户添加页面、查看用户信息页面、删除用户页面。部门级别的账号只可以查看用户信息页面。
细粒度权限,是指对具体资源访问的权限管理。也就是说不同的用户访问到同一张资源表中的资源数据是不同的,这是属于数据级别的权限控制。举个栗子:总经理可以访问用户表中的所有数据,而不同部门经理只能访问自己部门的那些用户信息。
三、权限管理的解决方案 13. | 程序员的进阶平台www.itzixi.com3.2 如何实现粗/细粒度的权限分配
粗粒度权限:单独的权限模块分配用户资源权限,可以按照角色里的权限统一分配或者按照单个资源来分配。
细粒度权限:需要控制在业务层,也就是在代码层控制,限制用户查询的时候增加id来进行判断,比如之前所说的查询用户按照该用户的所属部门id来进行查询,即可控制不同部门人员的访问限制。这个id需要从action/controller层传到service层到dao层进行传输查询。
14. | 程序员的进阶平台www.itzixi.com4.1 基于拦截器的权限管理
这是最常见最常用的权限管理方式,基于拦截器拦截url从而限制用户访问对应的资源,
例如strtus的拦截器,springmvc的拦截器等都是可以做的。
四、权限管理的方式 15. | 程序员的进阶平台www.itzixi.com4.2 基于权限框架的权限管理
4.2.1 shiro权限框架
shiro是一款轻量级的权限框架,可以作用于J2SE、J2EE等,整合到spring中也十分强大。
4.2.2 spring security
spring 官方提供的安全框架,很强大,稍重,也是基于拦截用户请求的。
*注:使用权限框架进行权限管理是减少开发成本的一种方式,也减少了维护成本
16. | 程序员的进阶平台www.itzixi.com5.1 What is shiro?
Shiro是apache的一个开源权限管理的框架,它实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架
使用shiro来实现权限管理,可以非常有效的提高团队开发效率,从而降低开发成本以及维护成本。
五、shiro 17. | 程序员的进阶平台www.itzixi.com5.2、shiro 的基本架构Subject:主体,subject记录了当前操作的用户信息。我们对这个subject进行认证和授权。
SecurityManager:安全管理者,主体进行认证和授权都 是通过securityManager进行。是shiro的核心。
Authenticator:用于用户登录认证的,主体的认证是否通过都是由authenticator来做的。
Authorizer:授权鉴权,主体进行授权是否通过由authorizer来做的。
Session Manager:web应用中需要对session进行会话管理,那么shiro本身也有session管理的方式,用sessionManager来做的。这个可以用来实现单点登录sso。
SessionDao: 统一管理session,不论是分布式还是单应用都帮你做好了。比如对session的塞入一个用户和删除一个用户,而这个是用的redis这样的缓存的话,就要用到sessionDAO。
Cache Manager:缓存管理者,将用户权限信息缓存起来,这样可以提高性能,不必频繁地读取数据库,减少数据库压力。ehcache,redis等
realm:领域,范围,可以理解为数据源,认证和授权的相关数据都是存在这里的。
cryptography:密码管理,提供各种类型的加密算法,比如MD5等。 18. | 程序员的进阶平台www.itzixi.com1.3.2
org.apache.shiro
shiro-core
${shiro.version}
org.apache.shiro
shiro-web
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
org.apache.shiro
shiro-ehcache
${shiro.version}
org.apache.shiro
shiro-quartz
${shiro.version}
5.3、是用shiro所需要的jar包 19. | 程序员的进阶平台www.itzixi.com6.1 认证流程
1、创建token令牌,token包含了用户提交的账号和密码
2、执行subject.login(token),最终由securityManager通过Authenticator进行认证
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro默认自带)
4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
* 进行密码对比过程
我们这里使用的是ini配置,真正情况下是根据token中的身份信息去查询数据库比对六、使用shiro进行用户登录(认证) 20. | 程序员的进阶平台www.itzixi.com6.2.1 创建shiro的ini文件,通过此文件来创建安全管理者的工厂类
6.2、shiro登录的入门简单栗子6.2.2 在ini中配置用户信息 21. | 程序员的进阶平台www.itzixi.com6.2.3 认证代码执行主体认证时候的异常信息
org.apache.shiro.authc.UnknownAccountException
org.apache.shiro.authc.IncorrectCredentialsException
LockedAccountException
UnsupportedTokenException 22. | 程序员的进阶平台www.itzixi.com6.3 shiro登录自定义realm的实现真正开发从数据库中读取用户数据进行登录认证的,使用AuthorizingRealm接口来实现即可 23. | 程序员的进阶平台www.itzixi.com新建自定义realm类并且集成之前所说的抽象类 AuthorizingRealm 24. | 程序员的进阶平台www.itzixi.com登录认证的代码片段 25. | 程序员的进阶平台www.itzixi.com配置自定义realm的ini文件,最后使用ShiroLoginTest进行测试 26. | 程序员的进阶平台www.itzixi.com6.4 shiro登录认证时的密码加密本质就是对现有的一段明文进行散列算法后生成的新的一段文本,这个操作不可逆。常用的加密算法有MD5,SHA,RSA/RSA2,base64。
一般来说我们对加密的明文会进行一次“撒盐”操作,也就是通过另外一段随机字符对现有的明文进行散列加密。比如说现有的密码是 abc123,盐是 xyz,混合加密散列后的值不容易被破解。甚至还能进行多次散列。6.5 对密码加密,修改认证realm 27. | 程序员的进阶平台www.itzixi.com6.5 对密码加密,修改认证realm 28. | 程序员的进阶平台www.itzixi.com7.1 授权流程
1、构建安全管理者
2、执行subject.isPermitted(...),最终由securityManager通过Authorizer进行授权
3、Authorizer的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实权限,这里使用的是IniRealm(shiro默认自带)
七、使用shiro进行用户授权 29. | 程序员的进阶平台www.itzixi.com7.2 shiro授权的三种方式1. 通过编写硬代码来实现:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
// 有权限
} else {
// 无权限
}
2. 通过注解的形式来实现:
@RequiresRoles("admin")
public void operater() {
// 有权限
}
3. 在jsp上实现自定义标签(shiro提供,hasPermission除外)
30. | 程序员的进阶平台www.itzixi.com7.3 配置 shiroPermission.ini同样这个ini配置文件里的用户角色信息和资源信息当做数据库中的真实数据
# 配置用户
[users]
# 用户jack的密码是abc123,此用户具有admin和coder两个角色以下以此类推
jack=abc123,admin,coder
lucy=123abc,coder
lily=xyz123,hr
# 配置权限
[roles]
# 角色admin对资源user拥有query、add、mod、del权限
admin=user:query,user:add,user:mod,user:del
# 角色coder对资源user拥有add、del权限
coder=user:add,user:del
# 角色hr对资源user拥有query权限
hr=user:query
*此配置对应数据库的相应表结构
权限字符串的规则:
“资源标识符:操作:资源实例标识符”,
意思是对哪个资源的哪个实例具有什么操作,
“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。
举个栗子:
用户创建权限:user:add,或user:add:*
用户修改实例1001的权限:user:mod:1001
用户实例1001的所有权限:user:*:1001
一般来说都是用的一段和二段来进行权限规划的 31. | 程序员的进阶平台www.itzixi.com7.4 授权代码片段 32. | 程序员的进阶平台www.itzixi.com7.5 shiro实现自定义realm授权和自定义认证realm相同的原理
ini配置采用之前的自定义 33. | 程序员的进阶平台www.itzixi.com8.1 pom中加入shiro的依赖,引入jar
八、shiro整合spring springmvc mybatis
(基于 LeeCX 系统 :
https://github.com/leechenxiang/LeeCX
https://gitee.com/leechenxiang/LeeCX ) 34. | 程序员的进阶平台www.itzixi.com8.2 shiro的原理也是基于过滤器filter的,shiro有很多filters在web.xml中配置shirofilter 35. | 程序员的进阶平台www.itzixi.com8.3 配置 applicationContext-shiro.xml配置web.xml中fitler对应spring容器中的bean配置 安全管理者 securityManager配置自定义realm,用于数据库查询 36. | 程序员的进阶平台www.itzixi.com8.4 附:shiro过滤器过滤器简称对应的java类anonorg.apache.shiro.web.filter.authc.AnonymousFilterauthcorg.apache.shiro.web.filter.authc.FormAuthenticationFilterauthcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilterpermsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilterportorg.apache.shiro.web.filter.authz.PortFilterrestorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterrolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFiltersslorg.apache.shiro.web.filter.authz.SslFilteruserorg.apache.shiro.web.filter.authc.UserFilterlogoutorg.apache.shiro.web.filter.authc.LogoutFilteranon: /admins/**=anon 没有参数,表示可以匿名使用。
authc: /admins/user/**=authc 表示需要认证(登录)才能使用,没有参数
roles: /admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms: /admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest: /admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port: /admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic: /admins/user/**=authcBasic没有参数表示httpBasic认证
ssl: /admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user: /admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
注:
anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器 37. | 程序员的进阶平台www.itzixi.com8.5 用户登录//@PostMapping("/login")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public LeeJSONResult doPostlogin(String username, String password, HttpServletRequest request, HttpServletResponse response) {
if (StringUtils.isBlank(username)) {
return LeeJSONResult.errorMsg("用户名不能为空");
}
if (StringUtils.isBlank(password)) {
return LeeJSONResult.errorMsg("密码不能为空");
}
Subject user = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());
try {
user.login(token);
} catch (UnknownAccountException e) {
return LeeJSONResult.errorMsg("账号不存在");
} catch (DisabledAccountException e) {
return LeeJSONResult.errorMsg("账号未启用");
} catch (IncorrectCredentialsException e) {
return LeeJSONResult.errorMsg("密码错误");
} catch (RuntimeException e) {
return LeeJSONResult.errorMsg("未知错误,请联系管理员");
}
return LeeJSONResult.ok();
} 38. | 程序员的进阶平台www.itzixi.com 39. | 程序员的进阶平台www.itzixi.comvar registForm = $('#register-form');
var hdnContextPath = $("#hdnContextPath").val();
registForm.ajaxSubmit({
dataType: "json",
type: "post", // 提交方式 get/post
url: hdnContextPath + '/regist.action', // 需要提交的 url
data: registForm.serialize(),
success: function(data) {
// 登录成功或者失败的提示信息
if (data.status == 200 && data.msg == "OK") {
} else {
SweetAlert.error(data.msg);
}
},
error: function (response, ajaxOptions, thrownError) {
Error.displayError(response, ajaxOptions, thrownError);
}
}); 40. | 程序员的进阶平台www.itzixi.com@Autowired
private UserService userService;
/**
* 用于登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 从token中获取用户在表单中输入的域,即 用户名 以及 密码,以此来和数据库中的用户真实数据进行匹配
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
String username = userToken.getUsername();// 用户的登录名
String password = String.valueOf(userToken.getPassword());// 用户的密码
// 2. 根据用户输入的用户名和数据库进行匹配,查询数据库中的用户,如果查询不到,则返回一个null
SysUser user = userService.queryUserByUsername(username);
// 3. 判断数据库中查询出来的用户是否存在,不存在代表用户名密码错误;如果存在,则返回 AuthenticationInfo
if (user == null) {
return null;
}
String dbPassword = user.getPassword();
String dbSalt = user.getAuthSalt();
String userPassword = new Md5Hash(password, dbSalt, 2).toString();
if (!userPassword.equals(dbPassword)) {
// 抛出一个异常,密码不正确
throw new IncorrectCredentialsException();
}
// 4. 返回AuthenticationInfo
// 参数意义:
// Object principal: 用户对象,可以使一个对象类,或者一个字符串,存与session中
// Object credentials:密码
// 题外话:我们目前直接根据用户名密码来查询,所以这里直接放用户输入的密码即可;但是,也可疑直接用用户名来查询数据库中的用户,再然后进行密码的比对,如此则此处应该填写用户输入的密码
// String realmName:当前我们自定义realm的名称
AuthenticationInfo info = new SimpleAuthenticationInfo(user, password.toCharArray(), getName());
return info;
} 41. | 程序员的进阶平台www.itzixi.comvar registForm = $('#register-form');
var hdnContextPath = $("#hdnContextPath").val();
registForm.ajaxSubmit({
dataType: "json",
type: "post", // 提交方式 get/post
url: hdnContextPath + '/regist.action', // 需要提交的 url
data: registForm.serialize(),
success: function(data) {
// 登录成功或者失败的提示信息
if (data.status == 200 && data.msg == "OK") {
} else {
SweetAlert.error(data.msg);
}
},
error: function (response, ajaxOptions, thrownError) {
Error.displayError(response, ajaxOptions, thrownError);
}
}); 42. | 程序员的进阶平台www.itzixi.com8.6 用户登录连接数据库查询@Override
public SysUser queryUserByUsername(String username) {
SysUserExample userExample = new SysUserExample();
Criteria userCriteria = userExample.createCriteria();
userCriteria.andUsernameEqualTo(username);
List userList = userMapper.selectByExample(userExample);
if ( userList != null && !userList.isEmpty() ) {
return userList.get(0);
}
return null;
} 43. | 程序员的进阶平台www.itzixi.com8.7 用户退出登录 @RequestMapping(value = "/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login.action";
} 44. | 程序员的进阶平台www.itzixi.com8.8 用户注册@PostMapping("/regist")
@ResponseBody
public LeeJSONResult regist(String username, String password, HttpServletRequest request, HttpServletResponse response) {
SysUser user = new SysUser();
user.setUsername(username);
// 生成4位随机组合字符作为权限的盐
String authSalt = NumberUtil.getStringRandom(4);
user.setPassword(ShiroPasswordUtil.getShiroPassword(password, authSalt, 2));
user.setAuthSalt(authSalt);
user.setNickname(username);
// 验证一致,先注册用户
boolean registFlag = userService.saveUser(user);
if (!registFlag) {
return LeeJSONResult.errorMsg("注册失败!请返回首页重新注册.");
}
// 为了让用户在注册成功,直接访问首页,所以在注册超成功后,需要手动执行登陆
Subject userShiro = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());
userShiro.login(token);
return LeeJSONResult.ok();
} @Override
public boolean saveUser(SysUser user) {
String userId = sid.nextShort();
user.setId(userId);
user.setRegistTime(new Date());
user.setIsDelete(YesOrNo.NO.value);
int result = userMapper.insert(user);
return result == 1 ? true : false;
}var registForm = $('.register-form');
var hdnContextPath = $("#hdnContextPath").val();
registForm.ajaxSubmit({
dataType: "json",
type: "post", // 提交方式 get/post
url: hdnContextPath + '/regist.action', // 需要提交的 url
data: registForm.serialize(),
success: function(data) {
// 登录成功或者失败的提示信息
if (data.status == 200 && data.msg == "OK") {
SweetAlert.userRegistSuccess();
} else {
SweetAlert.error(data.msg);
}
},
error: function (response, ajaxOptions, thrownError) {
Error.displayError(response, ajaxOptions, thrownError);
}
}); 45. | 程序员的进阶平台www.itzixi.com8.9 用户授权8.10 用户授权链接数据库
SELECT
*
FROM
sys_permission
WHERE
id
IN
(
SELECT
sys_permission_id
FROM
sys_role_permission
WHERE
sys_role_id
IN
(SELECT sys_role_id FROM sys_user_role WHERE sys_user_id = #{id})
)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 从principals中获取当前用户
ActiveUser sessionUser = (ActiveUser)principals.getPrimaryPrincipal();
String userid = sessionUser.getUserId();
// 模拟从数据库中获取用户的权限(资源权限字符串)
List permissionList = null;
try {
permissionList = userService.findPermissionListByUserId(userid);
} catch (Exception e) {
e.printStackTrace();
}
List percodeList = new ArrayList();
for (SysPermission p : permissionList) {
percodeList.add(p.getPercode());
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(percodeList);
return simpleAuthorizationInfo;
} 46. | 程序员的进阶平台www.itzixi.com8.11 shiro标签库的使用<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ taglib uri="/shiro" prefix="shiro"%>
notAuthenticated
47. | 程序员的进阶平台www.itzixi.com8.12 扩展shiro标签库当前登录的主体用户名为:
用户是否认证登录过:是的 没有
是否游客:guest
是否登陆或者记住我:user 记住我
用户含有单一的资源权限:appuser:check
用户是否含有多个权限,如果含有其中一个或者多个,则显示:appuser:mng,appuser:getPersonal,appuser:toCheck,appuser:check,company:mng
48. | 程序员的进阶平台www.itzixi.com8.13 shiro注解形式简单讲解,使用场景@RequiresAuthentication
验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。
@RequiresUser
验证用户是否被记忆,user有两种含义:
一种是成功登录的(subject.isAuthenticated() 结果为true);
另外一种是被记忆的(subject.isRemembered()结果为true)。
@RequiresGuest
验证是否是一个guest的请求,与@RequiresUser完全相反。
换言之,RequiresUser == !RequiresGuest。
此时subject.getPrincipal() 结果为null.
@RequiresRoles
例如:@RequiresRoles("aRoleName");
void someMethod();
如果subject中有aRoleName角色才可以访问方法someMethod。如果没有这个权限则会抛出异常AuthorizationException。
@RequiresPermissions
例如: @RequiresPermissions({"file:read", "write:aFile.txt"} ) void someMethod();
要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法someMethod()。否则抛出异常AuthorizationException。
49. | 程序员的进阶平台www.itzixi.com8.14 shiro整合缓存ehcache授权的时候频繁查询数据库,此时我们需要使用shiro缓存,增加查询效率
用户认证通过后,第一次授权的时候会调用realm查询数据库,此时查询出来的权限数据会放入缓存;用户第二次及之后多次授权:系统不调用realm查询数据库,会直接从缓存中取出授权信息(权限标识符,realm中存放的数据info)
引入ehcache 50. | 程序员的进阶平台www.itzixi.com配置shiro的ehcache缓存文件
51. | 程序员的进阶平台www.itzixi.com配置缓存管理器*清除缓存,具体会在权限模块中操作,管理员修改用户权限后刷新用户的授权缓存信息/**
*
* @Description: 权限修改生效后,立即刷新清空缓存,则可以实现用户不退出生效新的权限
*
* @author leechenxiang
* @date 2016年9月29日 下午9:34:07
*/
public void clearCache() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
} 52. | 程序员的进阶平台www.itzixi.com8.15 配置session管理器,交由shiro来托管,不论web还是非web应用都可以支持session,为session提供增删改查,简化用户操作。*扩展sessin,配置更详细的属性
53. | 程序员的进阶平台www.itzixi.com8.16 shiro整合记住我功能 - rememberMe
54. | 程序员的进阶平台www.itzixi.com8.17 cookie/session 的原理在浏览器中,我们经常会涉及到一些数据的交换,比如登录邮箱,登录页面。通常会在此时设置7/30天内记住我,或者自动登录。那么信息是怎么被记录的呢,其实主要就是HTTP的cookie来处理了这一部分操作。Cookie是由HTTP服务器设置的,保存在浏览器中,HTTP协议是无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接,这个时候建立新连接的时候只需要从cookie中获取信息即可。
什么是有状态协议?
比如hettpRequest.getSession(); 这个session就是有状态的。*基于redis的spring session(实现方式:Jedis/redisTemplate)属于无状态session
session机制采用的是在服务器端保持状态的方案,而cookie是在客户端保持状态的方案,也被称之为会话跟踪机制。打开一次浏览器到关闭浏览器算是一次会话。
HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。此时,服务器无法从链接上跟踪会话。cookie可以跟踪会话,弥补HTTP无状态协议的不足。
cookie的类别:
cookie分为临时会话cookie和持久化会话cookie
临时会话cookie是指在不设定它的生命周期expires时的状态,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话cookie就会跟随浏览器而销毁。当关闭一个页面时(浏览器tab页面),不影响会话cookie的销毁。这样的临时会话是一次性的,玩完就扔。
持久化会话cookie是设定了一个生命周期expires,这个时候cookie就像一个吃的那样,是有保质期的,关闭浏览器或者重启电脑之后,它不会被销毁,直到设定的过期时间。对于这样的cookie,是可以在同一个浏览器中传递数据。比如我在本地浏览器上淘宝,选了几件商品放入购物车,今天不想买,过两天打开电脑访问淘宝,发现购物车里还是有之前放进去的数据,那么这个就是cookie的功能。
(淘宝现在是需要登录才能放入购物车,京东则不需要,可以试试)
cookie的过期时间设置:
cookie.setMaxAge(0); //不记录cookie
cookie.setMaxAge(-1); //会话级cookie,关闭浏览器失效
cookie.setMaxAge(60*60); //过期时间为1小时 55. | 程序员的进阶平台www.itzixi.com*京东购物车cookie 56. | 程序员的进阶平台www.itzixi.com8.18 使用redisTemplate替代jedisPool
org.springframework.data
spring-data-redis
${spring-data-redis.version}
org.apache.commons
commons-pool2
${apache-commons-pool2}
1.8.7.RELEASE
2.4.2 注意点:
2.9.0 57. | 程序员的进阶平台www.itzixi.comapplicationContext-redis以及属性文件redis.properties:
#single redis
redis.single.client.host=192.168.1.191
redis.single.client.port=6379
redis.password=123456
#最大分配的对象数
redis.pool.maxActive=1024
#最大能够保持idel状态的对象数
redis.pool.maxIdle=200
#当池内没有返回对象时,最大等待时间
redis.pool.maxWait=1000
#当调用borrow Object方法时,是否进行有效性检查
redis.pool.testOnBorrow=true
#当调用return Object方法时,是否进行有效性检查
redis.pool.testOnReturn=true 58. | 程序员的进阶平台www.itzixi.com8.19 shiro使用redis替代ehcache缓存
59. | 程序员的进阶平台www.itzixi.com附:认证信息进行缓存
自定义realm中的构造函数:
public ShiroDBRealm(CacheManager cacheManager) {
super(cacheManager);
} 60. | 程序员的进阶平台www.itzixi.com8.20 shiro整合验证码 61. | 程序员的进阶平台www.itzixi.com8.21 shiro自定义过滤器,实现同一账户不同地点只能登录一人
原理:当A登录系统后,B也登录了,这个这个时候获取把之前的session给剔除,session放入缓存,一个账户作为一个有序的集合。 62. | 程序员的进阶平台www.itzixi.com九. 集群9.1 集群的概念
9.2 反向代理服务器nginx的介绍
9.3 nginx的安装
9.4 nginx的集群配置
9.5 基于redis的shiro系统集群测试 63. | 程序员的进阶平台www.itzixi.com十、 单点登录 SSO 64. | 程序员的进阶平台www.itzixi.com十一、权限模块
用户管理
权限管理
部门管理 65. | 程序员的进阶平台www.itzixi.com待续
关注微信公众号获得更多技术资讯