80端口被封怎么做网站,个人网站做支付接口,网站订单系统模板下载,网页小游戏打不开Spring Boot(十四)#xff1a;spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro。安全应该是互联网公司的一道生命线#xff0c;几乎任何的公司都会涉及到这方面的需求。在Java领域一般有Spring Security、Apache Shiro等安全框架#xff0c;但是由…Spring Boot(十四)spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro。安全应该是互联网公司的一道生命线几乎任何的公司都会涉及到这方面的需求。在Java领域一般有Spring Security、Apache Shiro等安全框架但是由于Spring Security过于庞大和复杂大多数公司会选择Apache Shiro来使用这篇文章会先介绍一下Apache Shiro在结合Spring Boot给出使用案例。 一、Apache Shiro 1What is Apache Shiro? Apache Shiro是一个功能强大、灵活的开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。 Apache Shiro的首要目标是易于使用和理解。安全通常很复杂甚至让人感到很痛苦但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性向外暴露简单、直观的API来简化开发人员实现应用程序安全所花费的时间和精力。 Shiro能做什么呢 验证用户身份用户访问权限控制比如1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限在非 web 或 EJB 容器的环境下可以任意使用Session API可以响应认证、访问控制或者 Session 生命周期中发生的事件可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)支持单点登录(SSO)功能支持提供“Remember Me”服务获取用户关联信息而无需登录…等等——都集成到一个有凝聚力的易于使用的API。 Shiro 致力在所有应用环境下实现上述功能小到命令行应用程序大到企业应用中而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去但也可以在它们之外的任何环境下开箱即用。 2Apache Shiro Features 特性 Apache Shiro是一个全面的、蕴含丰富功能的安全框架。下图为描述Shiro功能的框架图 Authentication认证, Authorization授权, Session Management会话管理, Cryptography加密被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧 Authentication认证用户身份识别通常被称为用户“登录”Authorization授权访问控制。比如某个用户是否具有某个操作的使用权限。Session Management会话管理特定于用户的会话管理,甚至在非web 或 EJB 应用程序。Cryptography加密在对数据源使用加密算法加密的同时保证易于使用。还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持 Web支持Shiro 提供的 web 支持 api 可以很轻松的保护 web 应用程序的安全。缓存缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。并发Apache Shiro 支持多线程应用程序的并发特性。测试支持单元测试和集成测试确保代码和预想的一样安全。“Run As”这个功能允许用户假设另一个用户的身份(在许可的前提下)。“Remember Me”跨 session 记录用户的身份只有在强制需要时才需要登录。注意 Shiro不会去维护用户、维护权限这些需要我们自己去设计/提供然后通过相应的接口注入给Shiro 3High-Level Overview 高级概述 在概念层Shiro 架构包含三个主要的理念Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用我们将在下面依次对其进行描述。 Subject当前用户Subject 可以是一个人但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。SecurityManager管理所有SubjectSecurityManager 是 Shiro 架构的核心配合内部安全组件共同组成安全伞。Realms用于进行权限信息的验证我们自己实现。Realm 本质上是一个特定的安全 DAO它封装与数据源连接的细节得到Shiro 所需的相关的数据。在配置 Shiro 的时候你必须指定至少一个Realm 来实现认证authentication和/或授权authorization。我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份Authorization 是授权访问控制用于对用户进行的操作授权证明该用户是否允许进行当前操作如访问某个链接某个资源文件等。 二、快速上手 1基础信息 1pom包依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependencydependencygroupIdnet.sourceforge.nekohtml/groupIdartifactIdnekohtml/artifactIdversion1.9.22/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion1.4.0/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdscoperuntime/scope/dependency
/dependencies 重点是 shiro-spring包 2配置文件 spring:datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: rootdriver-class-name: com.mysql.jdbc.Driverjpa:database: mysqlshow-sql: truehibernate:ddl-auto: updatenaming:strategy: org.hibernate.cfg.DefaultComponentSafeNamingStrategyproperties:hibernate:dialect: org.hibernate.dialect.MySQL5Dialectthymeleaf:cache: falsemode: LEGACYHTML5 thymeleaf的配置是为了去掉html的校验 3页面 我们新建了六个页面用来测试 index.html 首页login.html 登录页userInfo.html 用户信息页面userInfoAdd.html 添加用户页面userInfoDel.html 删除用户页面403.html 没有权限的页面除过登录页面其它都很简单大概如下 !DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/title
/head
body
h1index/h1
/body
/html 2RBAC RBAC 是基于角色的访问控制Role-Based Access Control 在 RBAC 中权限与角色相关联用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的权限赋予给角色而把角色又赋予用户这样的权限设计很清楚管理起来很方便。 采用jpa技术来自动生成基础表格对应的entity如下 用户信息 Entity
public class UserInfo implements Serializable {IdGeneratedValueprivate Integer uid;Column(unique true)private String username;//帐号private String name;//名称昵称或者真实姓名不同系统不同定义private String password; //密码;private String salt;//加密密码的盐private byte state;//用户状态,0:创建未认证比如没有激活没有输入验证码等等--等待验证的用户 , 1:正常状态,2用户被锁定.ManyToMany(fetch FetchType.EAGER)//立即从数据库中进行加载数据;JoinTable(name SysUserRole, joinColumns { JoinColumn(name uid) }, inverseJoinColumns {JoinColumn(name roleId) })private ListSysRole roleList;// 一个用户具有多个角色// 省略 get set 方法} 角色信息 Entity
public class SysRole {IdGeneratedValueprivate Integer id; // 编号private String role; // 角色标识程序中判断使用,如admin,这个是唯一的:private String description; // 角色描述,UI界面显示使用private Boolean available Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户//角色 -- 权限关系多对多关系;ManyToMany(fetch FetchType.EAGER)JoinTable(nameSysRolePermission,joinColumns{JoinColumn(nameroleId)},inverseJoinColumns{JoinColumn(namepermissionId)})private ListSysPermission permissions;// 用户 - 角色关系定义;ManyToManyJoinTable(nameSysUserRole,joinColumns{JoinColumn(nameroleId)},inverseJoinColumns{JoinColumn(nameuid)})private ListUserInfo userInfos;// 一个角色对应多个用户// 省略 get set 方法} 权限信息 Entity
public class SysPermission implements Serializable {IdGeneratedValueprivate Integer id;//主键.private String name;//名称.Column(columnDefinitionenum(menu,button))private String resourceType;//资源类型[menu|button]private String url;//资源路径.private String permission; //权限字符串,menu例子role:*button例子role:create,role:update,role:delete,role:viewprivate Long parentId; //父编号private String parentIds; //父编号列表private Boolean available Boolean.FALSE;ManyToManyJoinTable(nameSysRolePermission,joinColumns{JoinColumn(namepermissionId)},inverseJoinColumns{JoinColumn(nameroleId)})private ListSysRole roles;// 省略 get set 方法} 根据以上的代码会自动生成user_info用户信息表、sys_role角色表、sys_permission权限表、sys_user_role用户角色表、sys_role_permission角色权限表这五张表为了方便测试我们给这五张表插入一些初始化数据 INSERT INTO user_info (uid,username,name,password,salt,state) VALUES (1, admin, 管理员, d3c59d25033dbf980d29554025c23a75, 8d78869f470951332959580424d4bf4f, 0);
INSERT INTO sys_permission (id,available,name,parent_id,parent_ids,permission,resource_type,url) VALUES (1,0,用户管理,0,0/,userInfo:view,menu,userInfo/userList);
INSERT INTO sys_permission (id,available,name,parent_id,parent_ids,permission,resource_type,url) VALUES (2,0,用户添加,1,0/1,userInfo:add,button,userInfo/userAdd);
INSERT INTO sys_permission (id,available,name,parent_id,parent_ids,permission,resource_type,url) VALUES (3,0,用户删除,1,0/1,userInfo:del,button,userInfo/userDel);
INSERT INTO sys_role (id,available,description,role) VALUES (1,0,管理员,admin);
INSERT INTO sys_role (id,available,description,role) VALUES (2,0,VIP会员,vip);
INSERT INTO sys_role (id,available,description,role) VALUES (3,1,test,test);
INSERT INTO sys_role_permission VALUES (1, 1);
INSERT INTO sys_role_permission (permission_id,role_id) VALUES (1,1);
INSERT INTO sys_role_permission (permission_id,role_id) VALUES (2,1);
INSERT INTO sys_role_permission (permission_id,role_id) VALUES (3,2);
INSERT INTO sys_user_role (role_id,uid) VALUES (1,1); 3Shiro 配置 首先要配置的是ShiroConfig类Apache Shiro 核心通过 Filter 来实现就好像SpringMvc 通过DispachServlet 来主控制一样。 既然是使用 Filter 一般也就能猜到是通过URL规则来进行过滤和权限校验所以我们需要定义一系列关于URL的规则和访问权限。 1ShiroConfig Configuration
public class ShiroConfig {Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {System.out.println(ShiroConfiguration.shirFilter());ShiroFilterFactoryBean shiroFilterFactoryBean new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);//拦截器.MapString,String filterChainDefinitionMap new LinkedHashMapString,String();// 配置不会被拦截的链接 顺序判断filterChainDefinitionMap.put(/static/**, anon);//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put(/logout, logout);//!-- 过滤链定义从上向下顺序执行一般将/**放在最为下边 --:这是一个坑呢一不小心代码就不好使了;//!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--filterChainDefinitionMap.put(/**, authc);// 如果不设置默认会自动寻找Web工程根目录下的/login.jsp页面shiroFilterFactoryBean.setLoginUrl(/login);// 登录成功后要跳转的链接shiroFilterFactoryBean.setSuccessUrl(/index);//未授权界面;shiroFilterFactoryBean.setUnauthorizedUrl(/403);shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}Beanpublic MyShiroRealm myShiroRealm(){MyShiroRealm myShiroRealm new MyShiroRealm();return myShiroRealm;}Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager new DefaultWebSecurityManager();securityManager.setRealm(myShiroRealm());return securityManager;}
} Filter Chain定义说明 一个URL可以配置多个Filter使用逗号分隔当设置多个过滤器时全部验证通过才视为通过部分过滤器可指定参数如permsrolesShiro内置的FilterChain anon:所有url都都可以匿名访问authc: 需要认证才能进行访问user:配置记住我或认证通过可以访问2登录认证实现 在认证、授权内部实现机制中都有提到最终处理都将交给Real进行处理。因为在Shiro中最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说Realm是专用于安全框架的DAO. Shiro的认证过程最终会交由Realm执行这时会调用Realm的getAuthenticationInfo(token)方法。 该方法主要执行以下操作: 检查提交的进行认证的令牌信息根据令牌信息从数据源(通常为数据库)中获取用户信息对用户信息进行匹配验证。验证通过将返回一个封装了用户信息的AuthenticationInfo实例。验证失败则抛出AuthenticationException异常信息。而在我们的应用程序中要做的就是自定义一个Realm类继承AuthorizingRealm抽象类重载doGetAuthenticationInfo()重写获取用户信息的方法。 doGetAuthenticationInfo的重写 Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {System.out.println(MyShiroRealm.doGetAuthenticationInfo());//获取用户的输入的账号.String username (String)token.getPrincipal();System.out.println(token.getCredentials());//通过username从数据库中查找 User对象如果找到没找到.//实际项目中这里可以根据实际情况做缓存如果不做Shiro自己也是有时间间隔机制2分钟内不会重复执行该方法UserInfo userInfo userInfoService.findByUsername(username);System.out.println(-----userInfouserInfo);if(userInfo null){return null;}SimpleAuthenticationInfo authenticationInfo new SimpleAuthenticationInfo(userInfo, //用户名userInfo.getPassword(), //密码ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//saltusernamesaltgetName() //realm name);return authenticationInfo;
} 3链接权限的实现 shiro的权限授权是通过继承AuthorizingRealm抽象类重载doGetAuthorizationInfo();当访问到页面的时候链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行所以如果只是简单的身份认证没有权限的控制的话那么这个方法可以不进行实现直接返回null即可。在这个方法中主要是使用类SimpleAuthorizationInfo进行角色的添加和权限的添加。 Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println(权限配置--MyShiroRealm.doGetAuthorizationInfo());SimpleAuthorizationInfo authorizationInfo new SimpleAuthorizationInfo();UserInfo userInfo (UserInfo)principals.getPrimaryPrincipal();for(SysRole role:userInfo.getRoleList()){authorizationInfo.addRole(role.getRole());for(SysPermission p:role.getPermissions()){authorizationInfo.addStringPermission(p.getPermission());}}return authorizationInfo;
} 当然也可以添加set集合roles是从数据库查询的当前用户的角色stringPermissions是从数据库查询的当前用户对应的权限: authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(stringPermissions); 就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “perms[权限添加]”);就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “roles[100002]perms[权限添加]”);就说明访问/add这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问。 4登录实现 登录过程其实只是处理异常的相关信息具体的登录验证交给shiro来处理 RequestMapping(/login)
public String login(HttpServletRequest request, MapString, Object map) throws Exception{System.out.println(HomeController.login());// 登录失败从request中获取shiro处理的异常信息。// shiroLoginFailure:就是shiro异常类的全类名.String exception (String) request.getAttribute(shiroLoginFailure);System.out.println(exception exception);String msg ;if (exception ! null) {if (UnknownAccountException.class.getName().equals(exception)) {System.out.println(UnknownAccountException -- 账号不存在);msg UnknownAccountException -- 账号不存在;} else if (IncorrectCredentialsException.class.getName().equals(exception)) {System.out.println(IncorrectCredentialsException -- 密码不正确);msg IncorrectCredentialsException -- 密码不正确;} else if (kaptchaValidateFailed.equals(exception)) {System.out.println(kaptchaValidateFailed -- 验证码错误);msg kaptchaValidateFailed -- 验证码错误;} else {msg else exception;System.out.println(else -- exception);}}map.put(msg, msg);// 此方法不处理登录成功,由shiro进行处理return /login;
} 4测试 1、编写好后就可以启动程序访问http://localhost:8080/userInfo/userList页面由于没有登录就会跳转到http://localhost:8080/login页面。登录之后就会跳转到index页面登录后直接在浏览器中输入http://localhost:8080/userInfo/userList访问就会看到用户信息。上面这些操作时候触发MyShiroRealm.doGetAuthenticationInfo()这个方法也就是登录认证的方法。 2、登录admin账户访问http://127.0.0.1:8080/userInfo/userAdd显示用户添加界面访问http://127.0.0.1:8080/userInfo/userDel显示403没有权限。上面这些操作时候触发MyShiroRealm.doGetAuthorizationInfo()这个方面也就是权限校验的方法。 3、修改admin不同的权限进行测试 shiro很强大这仅仅是完成了登录认证和权限管理这两个功能更多内容。 转载于:https://www.cnblogs.com/lizm166/p/10270547.html