dedecms导入网站模板下载,html指什么,建设公司网站费用怎么做账,做企业网站有什么用文章目录 1.JavaWeb中的权限控制2.权限框架核心知识ACL和RBAC2.1.ACL和RBAC简介2.2主流权限框架介绍 3.Shiro架构和基本概念3.1.Shiro的4大核心模块3.2.Shiro权限控制运行流程 4.Shiro简单API案例4.1.项目搭建所需依赖4.2.Shiro认证简单实操4.3.Shiro授权简单实操 5.安全数据来… 文章目录 1.JavaWeb中的权限控制2.权限框架核心知识ACL和RBAC2.1.ACL和RBAC简介2.2主流权限框架介绍 3.Shiro架构和基本概念3.1.Shiro的4大核心模块3.2.Shiro权限控制运行流程 4.Shiro简单API案例4.1.项目搭建所需依赖4.2.Shiro认证简单实操4.3.Shiro授权简单实操 5.安全数据来源Realm5.1.Realm简介和继承关系5.2.Shiro内置IniRealm权限验证5.3.Shiro内置JdbcRealm权限验证5.4.Shiro自定义Realm权限配置5.5.Shiro源码认证授权流程 6.Shiro权限认证Web案例6.1.Shiro内置的过滤器6.2.Shiro的Filter配置路径6.3.Shiro数据安全之数据加解密6.4.Shiro权限控制注解6.5.Shiro缓存模块讲解6.6.Shiro Session模块讲解 7.SpringBoot2.x整合Shiro7.1.数据库设计7.2.Maven项目搭建7.3.编写查询用户全部信息接口7.4.开发自定义CustomRealm7.5.ShiroFilterFactoryBean配置7.6.自定义SessionManager验证7.7.API拦截验证案例7.8.Shiro密码加密处理 8.权限控制性能提升8.1.自定义Shiro Filter过滤器8.2.Redis整合CacheManager8.3.Redis整合SessionManager8.4.ShiroConfig常用的Bean配置 9.分布式应用鉴权方式9.1.自定义SessionId 1.JavaWeb中的权限控制
1什么是权限控制
忽略特别细的概念比如权限能细分很多种功能权限数据权限管理权限等理解两个概念用户和资源让指定的用户只能操作指定的资源CRUD
2javaweb中怎么处理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws Exception {HttpServletRequest httpRequest(HttpServletRequest)request;HttpServletResponse httpResponse(HttpServletResponse)response;HttpSession sessionhttpRequest.getSession();if(session.getAttribute(username)!null){chain.doFilter(request, response);} else {httpResponse.sendRedirect(httpRequest.getContextPath()/login.jsp);}}2.权限框架核心知识ACL和RBAC
2.1.ACL和RBAC简介
ACLAccess Control List 访问控制列表 以前盛行的一种权限设计它的核心在于用户直接和权限挂钩优点简单易用、开发便捷缺点用户和权限直接挂钩导致在授权时的复杂性比较分散不便于管理案例常见的文件系统权限设计直接给用户加权类似Linux系统中的chmod RBACRole Based Access Control 基于角色的访问控制系统。权限与角色相关联用户通过适当的角色的成员而获得角色的权限优点简化了用户与权限的管理通过对用户进行分类使得角色与权限关联起来缺点开发相比ACL复杂案例基于RBAC模型的权限验证框架有Apache Shiro、Spring Security 总结权限设计不能太过于复杂否则性能会下降
2.2主流权限框架介绍
1什么是Spring Security
官网https://spring.io/projects/spring-security
Spring Security是一个能够基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean充分利用了Spring IoCDI和AOP功能为应用系统提供声明式的安全访问控制功能减少了企业系统安全控制编写大量重复代码的工作。2什么是Apache Shiro
官网https://github.com/apache/shiro
Apache Shiro是一个强大且易用的java安全框架执行身份验证、授权、密码和会话管理。使用shiro的易于理解的API可以快速、轻松的执行任何应用程序。两个的优缺点 Apache Shiro比Spring Security使用更简单Shiro功能强大、简洁、灵活不跟任何的框架或者容器绑定可以独立运行SpringSecurity对Spring体系支持比较友好脱离Spring体系开发很难SpringSecuity支持Oauth鉴权Shiro需要自己实现
3.Shiro架构和基本概念
3.1.Shiro的4大核心模块
1Shiro的四大核心模块分为身份认证、授权、会话管理和加密
身份认证 Authentication身份认证一般就是登录 授权 Authorization给用户分配角色或者访问某些资源的权限 会话管理 Session Management用户的会话管理员多数情况下是web Session 加密 Cryptogarphy数据加解密你如密码加解密等
2Shiro架构图 3.2.Shiro权限控制运行流程
1Shiro常见名称
Subject 我们把用户或者程序称为主体主体去访问资源或者系统 SecurityManager 安全管理器Subject的认证和授权都在安全管理器下进行 Authenticator 认证器主要负责Subject的认证 Realm 数据域Shiro和安全数据的连接器好比jdbc连接数据库通过realm获取认证授权的相关信息 Authorizer 授权器主要负责Subject的授权控制subject拥有的角色或者权限 Cryptography 加解密Shiro包含易于使用和理解的数据加密方法简化了很多复杂的API CacheManager 缓存管理器比如认证或者授权信息通过缓存进行管理提高性能 SessionManager 会话管理器大多数是web session SessionDAO SessionDAO即会话是对session会话的一套接口比如要将session存储到数据库。 4.Shiro简单API案例
4.1.项目搭建所需依赖
环境准备maven3.5jdk8springbootidea
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency
!--mysql starter 注意一定要把runtime去掉--
dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId
/dependency
!--测试模块starter--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope
/dependency
!--阿里巴巴数据源--
dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.6/version
/dependency
!--shiro相关依赖包--
dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion1.4.0/version
/dependency4.2.Shiro认证简单实操
1Shiro的认证流程
创建Security ManagerSecurity Manager是用来提供安全服务的所以在做shiro认证的时候要先创建此对象主题Subject提交请求给Security ManagerSecurity Manager调用Authenticator组件做认证Authenticator通过Realm来从数据源中获取认证数据 2编码测试
SpringBootTest
public class Test{//声明SecurityManagerDefaultSecurityManager securityManager new DefaultSecurityManager();//声明RealmSimpleAccountRealm accountRealm new SimpleAccountRealm();BeforeTestpublic void init(){accountRealm.addAccount(lixiang,123456);accountRealm.addAccount(lisi,123456);//构建环境securityManager.setRealm(accountRealm);}Testpublic void test(){SecurityUtils.setSecurityManager(securityManager);Subject subject SecurityUtils.getSubject();UsernameAndPasswordToken token new UsernameAndPasswordToken(lixiang,123456);subject.login(token);System.out.println(认证结果subject.isAuthenticated());}
}3测试结果 4.3.Shiro授权简单实操
1常用API
//是否有对应角色
subject.hasRole(root)//获取subject名
subject.getPrincipal()//检查是否有对应的角色无返回值直接在SecurityManager里面进行判断
subject.checkRole(admin)//检查是否有对应的角色
subject.hasRole(admin)//退出登录
subject.logout();2编码实操 Testvoid contextLoads() {SecurityUtils.setSecurityManager(securityManager);Subject subject SecurityUtils.getSubject();UsernamePasswordToken usernamePasswordToken new UsernamePasswordToken(lixiang,123456);subject.login(usernamePasswordToken);System.out.println(认证结果subject.isAuthenticated());System.out.println(获取subject主体的唯一标识subject.getPrincipal());//检查是否有对应角色无返回值直接在SecurityManager里面进行判断subject.checkRole(admin);//检查是否有对应的角色System.out.println(是否有对应角色subject.hasRole(admin));//退出登录subject.logout();System.out.println(认证结果subject.isAuthenticated());}5.安全数据来源Realm
5.1.Realm简介和继承关系
Realm的作用Shiro从Realm中获取安全的数据Realm中的两个概念 principal主体的标识可以有多个但是必须要有一个唯一性的常见的用户名、手机号、邮箱credential访问凭证一般就是密码 如果要自定义Realm继承AuthorizingRealm Realm顶级接口所有类的父接口CachingRelam带有缓存功能的Realm抽象类AuthenticatingRealm带有认证功能的Realm抽象类AuthorizingRealm带有授权功能的Realm抽象类SimpleAccountRealm提供一些简单的Realm认证TextConfigurationRealm提供文本形式的Realm认证IniRealm和PropertiesRealmTextConfigurationRealm的子类细化文本验证方式JdbcRealm与数据库交互的Realm认证DefaultLdapRealm根据LDAP进行身份验证
5.2.Shiro内置IniRealm权限验证
1新建shiro.ini文本文件编写规则
#用户模块对应用户名、密码、角色多个角色之间用逗号隔开
[users]
lixiang 123456,user
zhangsan 123456,admin,root#权限模块对应角色名称、对应权限多个权限用分隔
[roles]
user video:find,video:buy
admin video:*
root *2测试编码
Test
public void test(){//创建IniSecurityManagerFactory工厂实例,注意这块一定要是shiro下的包//IniSecurityManagerFactory这个类已经废弃了这里只做验证FactorySecurityManager factory new IniSecurityManagerFactory();//获取工厂实例SecurityManager securityManager factory.getInstance();//将securityManager设置到当前运行环境当中SecurityUtils.setSecurityManager(securityManager);//获取Subject对象Subject subject SecurityUtils.getSubject();//创建登录TokenUsernameAndPasswordToken token new UsernameAndPasswordToken(lixiang,123456);//验证subject.login(token);//判断是否有对应角色System.out.print(判断是否有对应角色subject.hasRole(admin));//判断是否有对应的权限System.out.print(判断是否有对应权限subject.isPermitted(video:find));//判断是否有对应的权限无返回值如果检验不通过则抛出异常//checkPermission(find:video)}5.3.Shiro内置JdbcRealm权限验证
1配置jdbcrealm.ini文件注意这块一定要是ANSI格式否则运行会抛错
#注意 文件格式必须为ini编码为ANSI#声明Realm指定realm类型
jdbcRealmorg.apache.shiro.realm.jdbc.JdbcRealm#配置数据源
#dataSourcecom.mchange.v2.c3p0.ComboPooledDataSourcedataSourcecom.alibaba.druid.pool.DruidDataSource# mysql-connector-java 5 用的驱动url是com.mysql.jdbc.Drivermysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver
dataSource.driverClassNamecom.mysql.cj.jdbc.Driver#避免安全警告
dataSource.urljdbc:mysql://120.76.62.13:3606/xdclass_shiro?characterEncodingUTF-8serverTimezoneUTCuseSSLfalsedataSource.usernametestdataSource.passwordXdclasstest#指定数据源
jdbcRealm.dataSource$dataSource#开启查找权限, 默认是false不会去查找角色对应的权限坑
jdbcRealm.permissionsLookupEnabledtrue#指定SecurityManager的Realms实现设置realms可以有多个用逗号隔开
securityManager.realms$jdbcRealm如果编码不是ANSI格式 2验证
配置文件中 jdbcRealm.permissionsLookupEnabledtrue 一定要设置成true默认是false不会去校验角色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdIHQpbi-1667452035146)(images/5.2(3)].jpg)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TjSb8TpI-1667452035146)(images/5.2(4)].jpg) Testvoid contextLoads() {//创建SecurityManager工厂FactorySecurityManager factory new IniSecurityManagerFactory(classpath:jdbcrealm.ini);//拿到工厂SecurityManager securityManager factory.getInstance();//将securityManager设置到当前运行环境当中SecurityUtils.setSecurityManager(securityManager);Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(jack,123);subject.login(token);System.out.println(认证结果subject.isAuthenticated());System.out.println(是否有对应的角色subject.hasRole(user));//查询是否有权限无返回值没有则抛异常//subject.checkPermission(video:delete);//查询是否有权限有返回值System.out.println(subject.isPermitted(video:delete));}5.4.Shiro自定义Realm权限配置
1自定义Realm步骤
(1)创建一个类继承AuthorizingRealm-AuthenticatingRealm-CachingRealm-Realm
(2)重写授权方法doGetAuthorizationInfo进行权限校验的时候会调用
(3)重写认证方法doGetAuthenticationInfo当用户登陆的时候会调用2对象介绍
UsernamePasswordToken 对应就是 shiro的token中有Principal和CredentialUsernameAndPasswordToken-HostAuthenticationToken-AuthenticationTokenSimpleAuthorizationInfo代表用户角色权限信息SimpleAuthenticationInfo代表该用户的认证信息
3编写自定义的Realm类
public class CustomRealm extends AuthorizingRealm {//userprivate final static MapString,String userMaps new HashMap();{userMaps.put(lixiang,123);userMaps.put(lisi,123);}//roles - permissionprivate final static MapString,SetString permissionMaps new HashMap();{SetString set1 new HashSet();SetString set2 new HashSet();set1.add(video:find);set1.add(video:buy);set2.add(video:add);set2.add(video:delete);permissionMaps.put(lixiang,set1);permissionMaps.put(lisi,set2);}//user - roleprivate final MapString,SetString roleMap new HashMap();{SetString set1 new HashSet();SetString set2 new HashSet();set1.add(role1);set1.add(role2);set2.add(root);roleMap.put(jack,set1);roleMap.put(xdclass,set2);}/*** 进行权限验证的时候调用* param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println(进行权限验证doGetAuthorizationInfo);String username principals.getPrimaryPrincipal().toString();SetString permissions getPermissionsfromDB(username);SetString roles getRolesfromDB(username);SimpleAuthorizationInfo simpleAuthorizationInfo new SimpleAuthorizationInfo();simpleAuthorizationInfo.setRoles(roles);simpleAuthorizationInfo.setStringPermissions(permissions);return simpleAuthorizationInfo;}/*** 通过用户名查找角色* param username* return*/private SetString getRolesfromDB(String username) {return roleMap.get(username);}/*** 通过用户名查找权限* param username* return*/private SetString getPermissionsfromDB(String username) {return permissionMaps.get(username);}/*** 进行身份验证的时候调用* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println( 进行身份验证doGetAuthenticationInfo);String username token.getPrincipal().toString();String pwd getPwdfromDB(username);if(.equals(pwd) || pwd null){return null;}SimpleAuthenticationInfo simpleAuthenticationInfo new SimpleAuthenticationInfo(username,pwd,this.getName());return simpleAuthenticationInfo;}private String getPwdfromDB(String username) {return userMaps.get(username).toString();}
}4测试
SecurityUtils.setSecurityManager(securityManager);
Subject subject SecurityUtils.getSubject();
UsernamePasswordToken token new UsernamePasswordToken(lixiang,123);
//登录
subject.login(token);
//唯一标识
System.out.println(用户名subject.getPrincipal());
System.out.println(是否有对应的角色subject.hasRole(role1));
System.out.println(是否有对应的权限subject.isPermitted(video:find));5.5.Shiro源码认证授权流程 认证流程
subject.login(token)DelegatingSubject.login(token)AuthenticatingSecurityManager.authenticate(token)AbstractAuthenticator.authenticate(token)ModulearRealmAuthenticator.doAuthenticate(token)ModulearRealmAuthenticator.doSingleRealmAuthentication(token)AuthenticatingRealm.getAuthenticationInfo(token) 鉴权流程
subject.checkRole(“admin”)DelegatingSubject.checkRole()AuthorizingSecurityManager.checkRole()ModulatRealmAuthorizer.checkRole()AuthorizingReaim,hasRole()AuthorizingRealm.doGetAuthorizationInfo()
6.Shiro权限认证Web案例
6.1.Shiro内置的过滤器
核心过滤器类DefaultFilter配置那个路径对应那个拦截器进行处理
authcorg.apache.shiro.web.filter.authc.FromAuthenticationFilter
- 需要认证登录才能访问userorg.apache.shiro.web.filter.authc.UserFilter
- 用户拦截器表示必须存在用户anonorg.apache.shiro.web.filter.authc.AnonymousFilter
- 匿名拦截器不需要登录即可访问的资源匿名用户或者游客一般用于过滤静态资源rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
- 角色授权拦截器验证用户是否拥有角色。
- 参数可以写多个多个参数时写roles[admin,user]当多个参数时必须每个参数都通过才算通过。permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
- 权限授权拦截器验证用户是否拥有权限
- 参数可写多个和角色多个是一致的authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
- httpBasic身份验证拦截器logoutorg.apache.shiro.web.filter.authc.LogoutFilter
- 退出拦截器执行后会执行跳转到shiroFilterFactoryBean.setLoginUrl()设置的urlportorg.apache.shiro.web.filter.authz.PortFilter
- 端口拦截器可通过的端口sslorg.apache.shiro.web.filter.authz.SslFilter
- ssl拦截器只有请求协议是https才能通过6.2.Shiro的Filter配置路径
路径支持通配符完整匹配注意匹配符不包括分隔符/路径通配符支持、*、**注意通配符匹配不包括目录分隔符/* 可以匹配所有。不加 * 可以进行前缀匹配但多个冒号就需要多个 * 来匹配
URL权限采取第一次匹配优先的方式,优先匹配靠前的规则
?匹配一个字符如/user? 匹配/user3但不匹配/user/
*匹配0个或多个字符串如/add*,匹配/addtest但不匹配/add/1
** : 匹配路径中的零个或多个路径如 /user/** 将匹 配 /user/xxx 或 /user/xxx/yyy例子
/user/**filter1
/user/addfilter2请求 /user/add 命中的是filter1拦截器性能问题通配符比字符串匹配会复杂点所以性能也会稍弱推荐使用字符匹配方式
6.3.Shiro数据安全之数据加解密
1为啥要加解密
明文数据容易泄露比如密码铭文存储万一泄露则会造成严重的后果
2什么是散列算法
一般叫hash简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数适合存储密码比如MD5
3什么是salt盐
如果直接通过散列函数得到的加密数据容易被对应解密网站暴力破解一般会在应用层序里面加特殊的自动进行处理比如用户id等等唯一标识的东西。
4Shiro里面CredentialsMatcher用来验证密码是否正确
源码AuthenticatingRealm - assertCredentialsMatch()5自定义验证规则
一般会自定义验证规则Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher new HashedCredentialsMatcher();//散列算法使用MD5算法;hashedCredentialsMatcher.setHashAlgorithmName(md5);//散列的次数比如散列两次相当于 md5(md5(xxx));hashedCredentialsMatcher.setHashIterations(2);return hashedCredentialsMatcher;}6.4.Shiro权限控制注解
RequiresRoles(value{admin,editor},logicalLogical.AND)
需要角色admin和editor两个角色同时满足RequiresRoles(value{admin,editor},logicalLogical.OR)
需要角色admin或editor两个角色其中一个满足RequiresAuthentication
已经授过权调用Subject.isAuthenticated()返回trueRequiresUser
身份验证或者通过记 住我登录的查用API
subject.hasRole(xxx)
subject.isPermitted(xxx)
subject.isPermittedAll(xxx,xxx)
subject.checkRole(xxx)6.5.Shiro缓存模块讲解
AuthenticatingRealm 及 AuthorizingRealm 分别提供了对AuthenticationInfo 和 AuthorizationInfo 信息的缓存. 6.6.Shiro Session模块讲解
1什么是session会话
用户和程序直接的链接程序可以根据session识别到哪个用户和javaweb中的session类似
2什么是会话管理器SessionManager
会话管理器管理所有subject的所有操作是shiro的核心组件核心方法
//开启一个session
Session start(SessionContext context)
//指定key获取session
Session getSession(SessionKey key)3SessionDao会话存储/持久化 SessionDAO AbstractSessionDAO CachingSessionDAO EnterpeiseCacheSessionDAO MemorySessionDAO 核心方法
//创建
Serializable create(Session session)
//获取
Session readSession(Serializable sessionId) throws UnknownSessionException
//更新
void update(Session session)
//删除会话过期时调用
void delete(Session session)
//获取活跃的session
CollectionSession getActiveSessions()RememberMe
1.Cookie写到客户端并保存
2.通过调用subject.login()前设置 token.setRememberMe(true)
- subject.isAuthenticated() 表示用户进行了身份验证登录的即Subject.login 进行了登录
- subject.isRemembered() 表示用户是通过RememberMe登录的
- subject.isAuthenticated()true则 subject.isRemembered()false 两个互斥
- 总结特殊页面或者API调用才需要authc进行验证拦截该拦截器会判断用户是否是通过7.SpringBoot2.x整合Shiro
7.1.数据库设计 user表
CREATE TABLE user (id int(11) unsigned NOT NULL AUTO_INCREMENT,username varchar(128) DEFAULT NULL COMMENT 用户名,password varchar(256) DEFAULT NULL COMMENT 密码,create_time datetime DEFAULT NULL,salt varchar(128) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT4 DEFAULT CHARSETutf8;role表
CREATE TABLE role (id int(11) unsigned NOT NULL AUTO_INCREMENT,name varchar(128) DEFAULT NULL COMMENT 名称,description varchar(64) DEFAULT NULL COMMENT 描述,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT4 DEFAULT CHARSETutf8;user_role表
CREATE TABLE user_role (id int(11) unsigned NOT NULL AUTO_INCREMENT,role_id int(11) DEFAULT NULL,user_id int(11) DEFAULT NULL,remarks varchar(64) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT6 DEFAULT CHARSETutf8;permission表
CREATE TABLE permission (id int(11) unsigned NOT NULL AUTO_INCREMENT,name varchar(128) DEFAULT NULL COMMENT 名称,url varchar(128) DEFAULT NULL COMMENT 接口路径,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT6 DEFAULT CHARSETutf8;role_permission表
CREATE TABLE role_permission (id int(11) unsigned NOT NULL AUTO_INCREMENT,role_id int(11) DEFAULT NULL,permission_id int(11) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT8 DEFAULT CHARSETutf8;7.2.Maven项目搭建
创建SpringBoot项目引入依赖配置数据库 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion1.3.2/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--阿里巴巴druid数据源--dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.6/version/dependency!--spring整合shiro--dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion1.4.0/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/buildrepositoriesrepositoryidspring-snapshots/idnameSpring Snapshots/nameurlhttps://repo.spring.io/snapshot/urlsnapshotsenabledtrue/enabled/snapshots/repositoryrepositoryidspring-milestones/idnameSpring Milestones/nameurlhttps://repo.spring.io/milestone/url/repository/repositoriespluginRepositoriespluginRepositoryidspring-snapshots/idnameSpring Snapshots/nameurlhttps://repo.spring.io/snapshot/urlsnapshotsenabledtrue/enabled/snapshots/pluginRepositorypluginRepositoryidspring-milestones/idnameSpring Milestones/nameurlhttps://repo.spring.io/milestone/url/pluginRepository/pluginRepositories#数据库相关配置
spring.datasource.driver-class-name com.mysql.cj.jdbc.Driver
spring.datasource.urljdbc:mysql://192.168.10.88:3306/rbac_shiro?useUnicodetruecharacterEncodingutf-8useSSLfalse
spring.datasource.username root
spring.datasource.password 123456
#使用阿里巴巴druid数据源默认使用自带的
#spring.datasource.type com.alibaba.druid.pool.DruidDataSource
#开启控制台打印sql
mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl# mybatis 下划线转驼峰配置,两者都可以
#mybatis.configuration.mapUnderscoreToCamelCasetrue
mybatis.configuration.map-underscore-to-camel-casetrue7.3.编写查询用户全部信息接口
1实体类编写
User
/*** 用户表*/
public class User {private int id;private String username;private Date createTime;private String salt;private ListRole roleList;
}Role
/*** 角色表*/
public class Role {private int id;private String name;private String description;private ListPermission permissionList;
}UserRole
/*** 用户角色中间表*/
public class UserRole {private int id;private int userId;private int roleId;
}Permission
/*** 权限表*/
public class Permission {private int id;private String name;private String url;
}RolePermission
/*** 权限角色中间表*/
public class RolePermission {private int id;private int roleId;private int permissionId;
}2Mapper编写
UserMapper
public interface UserMapper {Select(select * from user where username #{username})User findByUsername(Param(username) String username);Select(select * from user where id #{id})User findById(Param(id) int id);Select(select * from user where username #{username} and password #{pwd})User findByUsernameAndPwd(Param(username) String username,Param(pwd) String pwd);
}RoleMapper
public interface RoleMapper {Select(select * from user_role where user_id #{userId})ListUserRole findRolesByUserId(Param(userId) int userId);Select(select * from role where id #{roleId})ListRole findRolesByRoleId(Param(roleId) int roleId);}PermissionMapper
public interface PermissionMapper {Select(select * from permission where id #{roleId})ListPermission findPermissionsByRoleId(Param(roleId) int roleId);
}3UserService编写
UserService
public interface UserService {/*** 获取用户全部信息包括角色权限* param username* return*/User findAllUserInfoByUsername(String username);/*** 获取用户基本信息* param userId* return*/User findSimpleUserInfoById(int userId);/*** 获取用户基本信息* param username* return*/User findSimpleUserInfoByUsername(String username);
}UserServiceImpl
Service
public class UserServiceImpl implements UserService {Autowiredprivate RoleMapper roleMapper;Autowiredprivate UserMapper userMapper;Autowiredprivate PermissionMapper permissionMapper;Overridepublic User findAllUserInfoByUsername(String username) {User user userMapper.findByUsername(username);ListUserRole userRoles roleMapper.findRolesByUserId(user.getId());ListRole roles new ArrayList();for (UserRole role : userRoles) {roles roleMapper.findRolesByRoleId(role.getRoleId());for (Role x : roles) {x.setPermissionList(permissionMapper.findPermissionsByRoleId(x.getId()));}}user.setRoleList(roles);return user;}Overridepublic User findSimpleUserInfoById(int userId) {return userMapper.findById(userId);}Overridepublic User findSimpleUserInfoByUsername(String username) {return userMapper.findByUsername(username);}
}5controller编写
RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;GetMapping(/find_user)public Object findUserInfo(RequestParam(username) String username){return userService.findAllUserInfoByUsername(username);}
}6测试 7.4.开发自定义CustomRealm
继承 AuthorizingRealm重写 doGetAuthorizationInfo重写 doGetAuthenticationInfo
public class CustomRealm extends AuthorizingRealm {Autowiredprivate UserService userService;/*** 用户鉴权的时候会调用* param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println(鉴权 doGetAuthorizationInfo);String username (String)principals.getPrimaryPrincipal();User userInfo userService.findAllUserInfoByUsername(username);//将角色权限放到对应的两个String类型集合中ListString stringRoleList new ArrayList();ListString stringPermissionList new ArrayList();ListRole roleList userInfo.getRoleList();for (Role role : roleList) {stringRoleList.add(role.getName());ListPermission permissionList role.getPermissionList();for (Permission permission : permissionList) {if(permission!null){stringPermissionList.add(permission.getName());}}}//拿到对应的角色集合权限集合封装SimpleAuthorizationInfo对象添加SimpleAuthorizationInfo simpleAuthorizationInfo new SimpleAuthorizationInfo();simpleAuthorizationInfo.addRoles(stringRoleList);simpleAuthorizationInfo.addStringPermissions(stringPermissionList);return simpleAuthorizationInfo;}/*** 用户认证登录的时候会调用* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println(认证 doGetAuthenticationInfo);//从token中拿到usernameString username (String)token.getPrincipal();User userInfo userService.findAllUserInfoByUsername(username);//获取密码String password userInfo.getPassword();if(password null || .equals(password)){return null;}//密码不为空说明认证成功封装SimpleAuthenticationInfo对象参数用户名密码class.getNamereturn new SimpleAuthenticationInfo(username,userInfo.getUsername(),this.getClass().getName());}
}7.5.ShiroFilterFactoryBean配置
shiroFilterFactoryBean-》 SecurityManager-》 CustomSessionManagerCustomRealm-》hashedCredentialsMatcher SessionManager DefaultSessionManager 默认实现常用于javaseServletContainerSessionManager: web环境DefaultWebSessionManager常用于自定义实现
Configuration
public class ShiroConfig {/*** 设置shiroFilter* param securityManager* return*/Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){System.out.println(执行 ShiroFilterFactoryBean.shiroFilter());ShiroFilterFactoryBean shiroFilterFactoryBean new ShiroFilterFactoryBean();//设置securityManagershiroFilterFactoryBean.setSecurityManager(securityManager);//设置登录成功后访问的路径shiroFilterFactoryBean.setLoginUrl(/pub/need_login);//设置登录成功后访问的urlshiroFilterFactoryBean.setSuccessUrl(/);//设置无权限访问的接口未授权无法访问接口shiroFilterFactoryBean.setUnauthorizedUrl(/pub/not_permit);//拦截器路径配置注意这里一定要用LinkedHashMapHashMap会出现偶尔无法拦截的情况MapString,String filterChainDefinitionMap new LinkedHashMap();//设置过滤器//登出过滤器用户退出时调用filterChainDefinitionMap.put(/logout,logout);//匿名过滤器游客模式filterChainDefinitionMap.put(/pub/**,anon);//登录过滤器用户只有登录才能访问filterChainDefinitionMap.put(/authc/**,authc);//管理员角色才能访问filterChainDefinitionMap.put(/admin/**,roles[admin]);//指定权限才能访问filterChainDefinitionMap.put(/video/update,perms[video_update]);filterChainDefinitionMap.put(/**,authc);shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 注入securityManager设置Realm和SessionManager* return*/Beanpublic SecurityManager securityManager(){DefaultSecurityManager securityManager new DefaultSecurityManager();securityManager.setRealm(customRealm());//注意如果不是前后端分离的项目就不需要设置sessionManagersecurityManager.setSessionManager(customSessionManager());return securityManager;}/*** 设置散列算法* return*/Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher new HashedCredentialsMatcher();//设设置散列算法md5hashedCredentialsMatcher.setHashAlgorithmName(md5);//设置散列次数hashedCredentialsMatcher.setHashIterations(2);return hashedCredentialsMatcher;}/*** 注入自定义的Realm* return*/Beanpublic CustomRealm customRealm(){CustomRealm customRealm new CustomRealm();customRealm.setCredentialsMatcher(hashedCredentialsMatcher());return customRealm;}/*** 注入自定义的SessionManager* return*/Beanpublic CustomSessionManager customSessionManager(){CustomSessionManager customSessionManager new CustomSessionManager();//超时时间默认30分钟不操作就会过期单位豪秒customSessionManager.setGlobalSessionTimeout(20000);return customSessionManager;}
}7.6.自定义SessionManager验证
public class CustomSessionManager extends DefaultWebSessionManager {private static final String TOKENtoken;//调用父类构造方法以防后续有人修改构造空构造覆盖会出问题public CustomSessionManager() {super();}Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {String sessionId WebUtils.toHttp(request).getHeader(TOKEN);//如果sessionId不为空就调用自定义的逻辑如果为空就调用父类的方法if(sessionId!null){//调用shiro内部的校验检测sessionId是否存在是否过期request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return sessionId;}else{return super.getSessionId(request,response);}}
}7.7.API拦截验证案例
1AdminController
RestController
RequestMapping(/admin)
public class AdminController {RequestMapping(/order)public JsonData findOrder(){MapString,String recordMap new HashMap();recordMap.put(SpringBoot入门到高级实战,300元);recordMap.put(Cloud微服务入门到高级实战,877元);recordMap.put(分布式缓存Redis,990元);return JsonData.buildSuccess(recordMap);}}2LogoutController
RestController
public class LogoutController {RequestMapping(/logout)public JsonData logout(){String username (String)SecurityUtils.getSubject().getPrincipal();if(username!null){SecurityUtils.getSubject().logout();return JsonData.buildSuccess(logout成功);}return JsonData.buildError(logout失败);}}3OrderController
RestController
RequestMapping(/authc)
public class OrderController {RequestMapping(/save)public JsonData findMyPlayRecord(){MapString ,String recordMap new HashMap();recordMap.put(SpringBoot入门到高级实战,第8章第1集);recordMap.put(Cloud微服务入门到高级实战,第4章第10集);recordMap.put(分布式缓存Redis,第10章第3集);return JsonData.buildSuccess(recordMap);}}4pubController
RestController
RequestMapping(/pub)
public class PubController {RequestMapping(/need_login)public JsonData needLogin(){return JsonData.buildSuccess(温馨提示请使用对应的账号登录,-2);}RequestMapping(not_permit)public JsonData notPermit(){return JsonData.buildSuccess(温馨提示拒绝访问没权限,-3);}RequestMapping(/index)public JsonData index(){ListString videoList new ArrayList();videoList.add(Mysql零基础入门到实战 数据库教程);videoList.add(Redis高并发高可用集群百万级秒杀实战);videoList.add(ZookeeperDubbo视频教程 微服务教程分布式教程);videoList.add(2019年新版本RocketMQ4.X教程消息队列教程);videoList.add(微服务SpringCloudDocker入门到高级实战);return JsonData.buildSuccess(videoList);}PostMapping(/login)public JsonData login(RequestBody UserQuery userQuery, HttpServletRequest request, HttpServletResponse response){Subject subject SecurityUtils.getSubject();System.out.println(userQuery:userQuery);MapString,Object info new HashMap();try {UsernamePasswordToken token new UsernamePasswordToken(userQuery.getUsername(),userQuery.getPwd());subject.login(token);info.put(session_id,subject.getSession().getId());return JsonData.buildSuccess(info,登录成功);}catch (Exception e){info.put(session_id,subject.getSession().getId());return JsonData.buildError(账号或密码错误);}}
}
5VideoController
RestController
RequestMapping(/video)
public class VideoController {RequestMapping(/update)public JsonData updateVideo(){return JsonData.buildSuccess(video 更新成功);}}6用户角色权限分配图 7测试 7.8.Shiro密码加密处理
SpringBootTest
class RbacShiroApplicationTests {Testvoid contextLoads() {//加密算法String hashName md5;//密码明文String pwd 123456;//加密函数使用shiro自带的SimpleHash hash new SimpleHash(hashName, pwd, null, 2);System.out.println(hash);}}8.权限控制性能提升
8.1.自定义Shiro Filter过滤器
1shiro默认的roles过滤器存在的问题 2自定义过滤器类继承AuthorizationFilter
public class CustomRolesAuthorizationFilter extends AuthorizationFilter {Overridepublic boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {Subject subject getSubject(request, response);String[] rolesArray (String[]) mappedValue;if (rolesArray null || rolesArray.length 0) {//no roles specified, so nothing to check - allow access.return true;}SetString roles CollectionUtils.asSet(rolesArray);//filterChainDefinitionMap.put(/admin/**,roles[admin,root])// shiro配置角色默认是与的关系需要都满足这里改成或的关系只要有其中一个即可for (String role : roles) {if (subject.hasRole(role)){return true;}}return false;}
}3ShiroConfig中配置自定义过滤器
//设置自定义过滤器
MapString, Filter filterMap new HashMap();
filterMap.put(customRolesFilter,new CustomRolesAuthorizationFilter());
shiroFilterFactoryBean.setFilters(filterMap);8.2.Redis整合CacheManager
Redis整合CacheManager为了提高性能避免每次都去库查
1加入shiro-redis依赖shiro和redis整合的jar包
!--shiro整合redis--
dependencygroupIdorg.crazycake/groupIdartifactIdshiro-redis/artifactIdversion3.1.0/version
/dependency2ShiroConfig中配置RedisManagerRedisCacheManagerSecruityManager /*** 加入RedisManager*/public RedisManager getRedisManager(){RedisManager redisManager new RedisManager();redisManager.setHost(192.168.10.88);redisManager.setPort(6379);return redisManager;}/*** 配置RedisCacheManager*/public RedisCacheManager cacheManager(){RedisCacheManager redisCacheManager new RedisCacheManager();redisCacheManager.setRedisManager(getRedisManager());//设置过期时间,单位秒redisCacheManager.setExpire(60);return redisCacheManager;}3改造现有逻辑自定义的Realm
doGetAuthorizationInfo 方法
原有String username (String)principals.getPrimaryPrincipal();User user userService.findAllUserInfoByUsername(username);改为User newUser (User)principals.getPrimaryPrincipal();User user userService.findAllUserInfoByUsername(newUser.getUsername());doGetAuthenticationInfo方法
原有
return new SimpleAuthenticationInfo(username, user.getPassword(), this.getClass().getName());改为
return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());8.3.Redis整合SessionManager
1加入SessionDAO的配置 /*** 配置SessionDAO*/public RedisSessionDAO sessionDAO(){RedisSessionDAO sessionDAO new RedisSessionDAO();//设置RedisManagersessionDAO.setRedisManager(getRedisManager());return sessionDAO;}2自定义的sessionManager中设置sessionDAO
//设置Session持久化RedisSessionManager
//注意如果不设置过期时间redis中存储也和shiro中session的默认过期时间保持一致
customSessionManager.setSessionDAO(sessionDAO());3注意传输的实体类都要实现Serializable接口否则会报错 8.4.ShiroConfig常用的Bean配置
1LifecycleBeanPostProcessor管理shiro一些bean的生命周期即bean初始化与销毁
Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();
}2AuthorizationAttributeSourceAdvisor加入注解的使用不加入这个AOP注解不生效RequiresGuest
Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());return authorizationAttributeSourceAdvisor;
}3DefaultAdvisorAutoProxyCreator用来扫描上下文寻找的所有Advistor通知器将符合条件的Advisor应用到切入点的Bean中需要在LifecycleBeanPostProcessor创建后才可以创建
Bean
DependsOn(lifecycleBeanPostProcessor)
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreatornew DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setUsePrefix(true);return defaultAdvisorAutoProxyCreator;
}9.分布式应用鉴权方式
9.1.自定义SessionId
Shiro 默认的sessionid生成 类名 SessionIdGenerator创建CustomSessionIdGenerator类实现 SessionIdGenerator 接口的方法
/** * 自定义session持久化 * return */
public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO redisSessionDAO new RedisSessionDAO(); redisSessionDAO.setRedisManager(getRedisManager()); //设置sessionid生成器 redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator()); //设置自定义的sessionIdGenerator return redisSessionDAO;
}