Shiro框架简单使用

    技术2022-07-11  141

    文章目录

    1. Shiro过滤器&标签简介Shiro过滤器Shiro的JSP标签 2. Shiro登陆认证(一)使用认证过滤器目标实现 3. Shiro登陆认证(二)完成登录认证(*)目标总结4. Shiro登陆认证(三)凭证匹配器-普通加密需求步骤总结 5. Shiro登陆认证(四)凭证匹配器-加盐加密需求什么是加盐加密步骤加盐加密实现总结 6. Shiro登陆认证(五)用户添加时密码加密7. Shiro登录注销需求实现 8. Shiro授权(一)介绍目标什么是授权,如何实现授权?Shiro授权校验有几种方式总结 9. Shiro授权(二)完成用户授权方法目标实现总结 10. Shiro授权(三)硬编码方式实现授权验证(了解)问题思考硬编码方式授权验证总结 11. Shiro授权(四)XML配置方式实现授权验证(推荐)需求总结 12. Shiro授权(五)注解方式实现授权验证(了解)需求注解方式实现授权验证 13. Shiro授权(六)Shiro标签实现授权验证编写test-auth.jsp测试 14. Shiro流程总结关键代码 15. 总结 Shiro简单使用

    1. Shiro过滤器&标签简介

    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.LogoutFilter

    Shiro的过滤器的作用,用于用户访问页面(URL)时进行权限控制。

    Shiro的JSP标签

    标签名称标签条件(满足条件,才显示标签内容)<shiro:authenticated >登录之后<shiro:notAuthenticated >不在登录状态时<shiro:guest >用户在没有RememberMe时<shiro:user >用户在RememberMe时<shiro:hasAnyRoles name=“abc,123” >在有abc或者123角色时<shiro:hasRole name=“abc”>拥有角色abc<shiro:lacksRole name=“abc”>没有角色abc<shiro:hasPermission name=“abc”>拥有权限资源abc<shiro:lacksPermission name=“abc”>没有abc权限资源<shiro:principal >默认显示用户名称

    Shiro的标签的作用,用于对JSP页面的元素(按钮,表单,超链接等)进行权限控制。

    2. Shiro登陆认证(一)使用认证过滤器

    目标

    在项目中使用认证过滤器拦截资源(该拦截的拦截,该放行的放行)

    实现

    在applicationContext-shiro.xml添加认证过滤器

    <!--1.Shiro处理认证授权逻辑的对象--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--安全管理器--> <property name="securityManager" ref="securityManager"/> <!--设置Shiro的过滤器--> <!-- 过滤的路径问题: /index.jsp : 准确拦截/index.jsp页面 /user/* : 模糊匹配,拦截user根目录下的所有资源(代表一级目录) /user/** : 模糊匹配,拦截user目录下的所有资源(代表任意级目录) 1. 认证相关的过滤器 anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面) authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面) --> <property name="filterChainDefinitions"> <value> /css/**=anon /img/**=anon /make/**=anon /plugins/**=anon /login.do=anon /**=authc </value> </property> <!--修改默认登录页面--> <property name="loginUrl" value="/login.jsp"/> </bean>

    anon:代表不认证也可以访问(匿名访问),通常对静态资源进行放行

    authc:代表必须通过认证才可以访问,通常对动态资源(controller,jsp页面)进行拦截,如果用户没有认证,Shiro会自动跳转到默认登录页面login.jsp页面,可以通过以下配置更改:

    <property name="loginUrl" value="/admin_login.jsp"/> 注意事项: /login.do因为是登录请求,必须使用anon过滤器放行!!!否则无法登录啦!

    3. Shiro登陆认证(二)完成登录认证(*)

    目标

    实现登陆认证。

    修改LoginController,通过shiro实现登陆认证编写AuthRealm,实现登陆认证测试

    1.修改LoginController,通过shiro实现登陆认证

    /** * 登录方法 * 1)URL: http://localhost:8080/login.do * 2)参数:email=eric&password=123 * 3)返回:/WEB-INF/pages/home/main.jsp */ @RequestMapping("/login") public String login(String email,String password){ //1.判断是否为空 if(StringUtils.isEmpty(email) || StringUtils.isEmpty(password)){ request.setAttribute("error","邮箱和密码不能为空"); //手动转发到login.jsp页面 return "forward:/login.jsp"; } //使用Shiro的登录认证逻辑 //1.获取Subject对象 Subject subject = SecurityUtils.getSubject(); //2.封装需要认证的信息(用户名和密码) UsernamePasswordToken token = new UsernamePasswordToken(email,password); //3.调用Subject的login方法进行认证 //4.判断login方法有无异常,有异常代表认证失败,无异常代表认证成功 try { subject.login(token); //无异常代表认证成功 //5.从Shiro的Subject中获取登录用户对象: subject.getPrincipal() User loginUser = (User)subject.getPrincipal(); //把登录用户对象存入session域 (注意:这里存入的loginUser对象,不是给Shiro使用的,是给我们自己业务使用的) session.setAttribute("loginUser",loginUser); //查询当前用户拥有的菜单(模块) List<Module> menuList = moduleService.findModuleByUser(loginUser); //菜单必须存入session域 session.setAttribute("menus",menuList); //跳转到主页 return "home/main"; } catch (UnknownAccountException e) { //UnknownAccountException: 该异常代表用户名不存在 request.setAttribute("error","Shrio:用户名不存在"); return "forward:/login.jsp"; } catch (IncorrectCredentialsException e) { //IncorrectCredentialsException: 该异常代表密码错误 request.setAttribute("error","Shrio:密码输入有误"); return "forward:/login.jsp"; } catch (Exception e) { // request.setAttribute("error","Shrio:服务器错误"); return "forward:/login.jsp"; } /*//2.判断email是否存在 User loginUser = userService.findByEmail(email); //3 email不存在,提示"用户名不存在" if(loginUser==null){ request.setAttribute("error","用户名不存在"); //手动转发到login.jsp页面 return "forward:/login.jsp"; } //4 email存在,继续判断password是否正确 if(loginUser.getPassword().equals(password)){ //5. password正确,登录成功,保存用户数据到session域中,跳转到主页 session.setAttribute("loginUser",loginUser); //查询当前用户拥有的菜单(模块) List<Module> menuList = moduleService.findModuleByUser(loginUser); //去除重复元素 removeDuplicate(menuList); //菜单必须存入session域 session.setAttribute("menus",menuList); // 路径: /WEB-INF/pages/home/main.jsp return "home/main"; }else{ //6.password不正确,提示"密码错误" request.setAttribute("error","密码错误"); //手动转发到login.jsp页面 return "forward:/login.jsp"; }*/ }

    2.编写AuthRealm,实现登陆认证

    /** * 自定义Realm */ public class AuthRealm extends AuthorizingRealm{ @Autowired private UserService userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权方法..."); return null; } //认证 (login方法固定执行doGetAuthenticationInfo方法) @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //System.out.println("执行认证方法..."); //1.判断用户名是否存在 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; String email = token.getUsername(); User loginUser = userService.findByEmail(email); if(loginUser==null){ //用户名不存在 //我们只需要return null即可,Shiro底层判断为null则抛出UnKnowAccountException异常 return null; } //2.返回数据库保存密码给Shiro,让Shiro判断密码是否正确 /** * Shiro底层获取SimpleAuthenticationInfo的参数,例如获取password判断和用户输入的密码是否一致 * 1)如果密码不一致,抛出IncorrectCredentialsException异常 * 2)如果密码一致, 把principal存入session域 */ //SimpleAuthenticationInfo: 封装认证数据对象 /** * 参数一: principal 登录用户对象,用于Subject.getPrincipal()方法获取 * 参数二: password 数据库的密码 * 参数三: realm的别名, 在多个Realm的情况下使用,用于区分用户表的。只有一张用户表不需要别名 */ return new SimpleAuthenticationInfo(loginUser,loginUser.getPassword(),""); } }

    总结

    Shiro登录认证的流程是怎样的?

    ​ LoginController的login方法调用Subject.login()

    ​ AuthRealm的认证方法,编写判断用户名和返回数据库密码的逻辑

    请简单描述Realm的认证方法(doGetAuthenticationInfo)代码流程?

    ​ 1)获取用户输入的信息

    ​ 2)判断用户名是否存在

    ​ 3)返回SimpleAuthenticationInfo,保存数据库密码和用户登录信息等

    4. Shiro登陆认证(三)凭证匹配器-普通加密

    需求

    目前对用户输入的密码,我们直接输入原文密码。这样不安全,现在我们需要对用户输入的密码md5加密 及 md5加盐加密。

    加密方式:

    1)普通加密(md5,sha1,sha2等)

    2)加盐加密(推荐使用)

    步骤

    1)对数据库密码进行md5加密 (略)

    2)在applicationContext-shiro.xml,添加加密认证配置

    3)测试

    1.略 2.在applicationContext-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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--1.Shiro处理认证授权逻辑的对象--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--安全管理器--> <property name="securityManager" ref="securityManager"/> <!--设置Shiro的过滤器--> <!-- 过滤的路径问题: /index.jsp : 准确拦截/index.jsp页面 /user/* : 模糊匹配,拦截user根目录下的所有资源(代表一级目录) /user/** : 模糊匹配,拦截user目录下的所有资源(代表任意级目录) 1. 认证相关的过滤器 anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面) authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面) --> <property name="filterChainDefinitions"> <value> /css/**=anon /img/**=anon /make/**=anon /plugins/**=anon /login.do=anon /**=authc </value> </property> <!--修改默认登录页面--> <property name="loginUrl" value="/login.jsp"/> </bean> <!--2.配置安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--配置Realm--> <property name="realm" ref="myRealm"/> </bean> <!--3.配置Realm--> <bean id="myRealm" class="cn.itcast.web.shiro.AuthRealm"> <!--配置凭证匹配器--> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!--创建Shiro自带的凭证匹配器--> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--指定需要加密的算法名称--> <property name="hashAlgorithmName" value="md5"/> </bean> </beans>

    总结

    请简单说出Shiro加密判断的执行流程

    ​ Subject.login(token) (未加密数据) -> AuthRealm的认证方法->返回数据库密码->先把token密码使用算法加密,再和数据库的密码匹配。

    5. Shiro登陆认证(四)凭证匹配器-加盐加密

    需求

    如果只是单纯使用md5或sha1进行加密,容易被人利用"彩虹表"撞库来破解密码,导致密码不安全!这时可以进行加盐加密来解决。

    什么是加盐加密

    加密流程: 使用md5算法加密1次 + 盐(变量,每个用户不同的)= 2次使用md5加密

    步骤

    1)编写代码对密码加盐加密

    2)编写自定义凭证匹配器

    3)在applicationContext-shiro.xml,添加自定义凭证匹配器

    4)测试

    加盐加密实现

    1)编写代码对密码加盐加密

    /** * 加盐加密的工具 */ public class Demo { public static void main(String[] args) { //1.原密码 String password = "123"; //2.盐 String salt = "lw@export.com"; //3.加盐加密 /** * 参数一:原密码 * 参数二:盐 * 参数三(可选):加密次数,默认1次 */ Md5Hash md5Hash = new Md5Hash(password,salt); System.out.println(md5Hash.toString()); } }

    2)编写自定义凭证匹配器

    /** * 自定义凭证匹配器 * 加盐加密的匹配 */ public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{ /** * 完成密码判断的逻辑 * @param token 包含了用户输入的密码 * @param info 包含了数据库的密码 * @return 密码判断结果 * true: 代表数据库的密码和用户输入的密码一致的 * false:代表数据库的密码和用户输入的密码不一致的 */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //1.获取用户输入的密码 UsernamePasswordToken userToken = (UsernamePasswordToken)token; String userPassword = new String(userToken.getPassword()); // ['1','2','3'] //获取用户邮箱 String email = userToken.getUsername(); //2.对用户输入的密码进行加盐加密 Md5Hash md5Hash = new Md5Hash(userPassword,email); String encodePwd = md5Hash.toString(); //3.获取数据库的密码 String dbPwd = (String)info.getCredentials(); //4.判断数据库的密码和第二步产生的密码是否一致,一致返回true,不一致返回false return dbPwd.equals(encodePwd); } }

    3)在applicationContext-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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--1.Shiro处理认证授权逻辑的对象--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--安全管理器--> <property name="securityManager" ref="securityManager"/> <!--设置Shiro的过滤器--> <!-- 过滤的路径问题: /index.jsp : 准确拦截/index.jsp页面 /user/* : 模糊匹配,拦截user根目录下的所有资源(代表一级目录) /user/** : 模糊匹配,拦截user目录下的所有资源(代表任意级目录 1. 认证相关的过滤器 anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面) authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面--> <property name="filterChainDefinitions"> <value> /css/**=anon /img/**=anon /make/**=anon /plugins/**=anon /login.do=anon /**=authc </value> </property> <!--修改默认登录页面--> <property name="loginUrl" value="/login.jsp"/> </bean> <!--2.配置安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--配置Realm--> <property name="realm" ref="myRealm"/> </bean> <!--3.配置Realm--> <bean id="myRealm" class="cn.itcast.web.shiro.AuthRealm"> <!--配置凭证匹配器--> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!--创建Shiro自带的凭证匹配器--> <!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!–指定需要加密的算法名称–> <property name="hashAlgorithmName" value="sha1"/> </bean>--> <!--创建自定义凭证匹配器--> <bean id="credentialsMatcher" class="cn.itcast.web.shiro.CustomCredentialsMatcher"/> </beans>

    总结

    1)数据库密码更新为加盐加密的密码

    2)自定义凭证匹配器类,编写判断密码逻辑

    3)applicationContextx-shiro.xml添加自定义凭证匹配器

    6. Shiro登陆认证(五)用户添加时密码加密

    注意:添加用户时候,需要对用户输入的密码加密、加盐,这样就可以使用添加的用户登陆了。 UserServiceImpl添加加密逻辑

    @Override public void save(User user) { //生成主键 user.setId(UUID.randomUUID().toString()); //对密码加盐加密 Md5Hash md5Hash = new Md5Hash(user.getPassword(),user.getEmail()); user.setPassword(md5Hash.toString()); userDao.save(user); }

    7. Shiro登录注销

    需求

    如果我们项目引入了Shiro,那么登录注销也要按照Shiro的方式来注销。

    实现

    Shiro的登录注销代码实现:

    /** * 登录注销 * 1)http://localhost:8080/logout.do * 2)参数:无 * 3)返回:/login.jsp */ @RequestMapping("/logout") public String logout(){ //删除session的登录数 session.removeAttribute("loginUser"); session.removeAttribute("menus"); //Shiro登录注销(底层:删除之前Shiro存入的session的key) Subject subject = SecurityUtils.getSubject(); subject.logout(); return "redirect:/login.jsp"; }

    8. Shiro授权(一)介绍

    目标

    什么是授权,如何实现授权?shiro授权校验有几种方式

    什么是授权,如何实现授权?

    授权,也叫做授权访问资源 或者说 校验是否可以访问资源

    登陆认证后,系统校验用户是否有权限访问资源,就叫授权。

    如何实现授权?分为以下两个步骤

    登陆认证成功后,获取用户的权限 (获取权限)访问资源时候,进行授权校验:用访问资源需要的权限去用户权限列表查找,如果存在,则有权限访问资源。(权限拦截)

    Shiro授权校验有几种方式

    shiro提供了四种方式实现权限校验:

    1) 硬编码方式(拦截方法)(非Web应用,Web应用)

    Subject subject = SecurityUtils.getSubject(); subject.checkPermission("部门管理");

    2) 过滤器(XML)配置方式(拦截url)(Web应用)

    /system/user/list.do = perms["用户管理"]

    3) 注解方式(拦截方法)(Web应用)

    @RequiresPermissions(“”)

    4) shiro提供的JSP标签((拦截页面元素:按钮,表格等))(Web应用)

    <shiro:hasPermission name="用户管理"> <a href="#">用户管理</a> </shiro:hasPermission>

    总结

    什么是授权?授权有哪两步?

    授权:用户认证成功后,进一步对访问资源进行权限控制

    授权的两步:

    1)获取用户权限 (买票)

    2)校验用户权限(验票)

    9. Shiro授权(二)完成用户授权方法

    目标

    实现自定义realm的doGetAuthorizationInfo()方法,返回用户已经具有的权限。

    实现

    //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //System.out.println("执行授权方法..."); //从Subject获取当前登录用户 Subject subject = SecurityUtils.getSubject(); User loginUser = (User)subject.getPrincipal(); //1、获取当前登录用户拥有的权限 List<Module> moduleList = moduleService.findModuleByUser(loginUser); //2、把当前登录用户的权限告诉Shiro框架 //2.1 创建授权对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //2.2 把模块存入授权对象 if(moduleList!=null && moduleList.size()>0){ for(Module module:moduleList){ if(!StringUtils.isEmpty(module.getName())) { //往授权对象存入授权信息(授权信息存入什么字符串? 字符串必须唯一的) info.addStringPermission(module.getName()); } } } return info; }

    总结

    Shiro的授权第一步:获取用户权限的逻辑

    1)查询当前用户拥有的权限(模块)

    2)把所有权限存入AuthorizationInfo对象(注意:存入的只是权限标记即可,标记必须唯一)

    3)返回AuthorizationInfo对象对象

    10. Shiro授权(三)硬编码方式实现授权验证(了解)

    问题

    我们以老王用户登录,可以看到老王拥有”用户管理“的菜单,他可以访问”用户“。但是老王其实没有”企业管理“的菜单,但是通过浏览器URL依然可以访问到”企业管理"内容。

    思考

    这种情况如何解决? 只有一种办法: 后台做授权验证,当用户有权限才可以访问,否则不能访问.

    硬编码方式授权验证

    只需要在控制器中添加如下2行代码即可: 测试结果 :报错,没有权限访问。

    总结

    Shiro硬编码方式的核心代码是什么?

    在需要验证的方法的前面加上Subject.checkPermissions(“权限标记”) 标记取决于存入时的内容

    11. Shiro授权(四)XML配置方式实现授权验证(推荐)

    需求

    通过XML配饰方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。

    Shiro授权过滤器实现授权验证(XML方式) 在applicationContext-shiro中

    <property name="filterChainDefinitions"> <value> /css/**=anon /img/**=anon /make/**=anon /plugins/**=anon /login.do=anon <!--左边为访问的资源 右边为需要的权限--> /system/user/list.do=perms["用户管理"] /company/list.do=perms["企业管理"] /**=authc </value> </property>

    总结

    Shiro的哪个过滤器可以实现授权验证?

    perms 格式:/system/user/list.do = perms[“用户管理”]

    12. Shiro授权(五)注解方式实现授权验证(了解)

    需求

    通过注解配置方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。

    注解方式实现授权验证

    现在通过注解实现权限控制,分为2步骤:

    在applicationContext-shiro.xml中开启shiro注解支持开启Aop自动代理(已经完成)在controller中使用@RequiresPermissions(“”)注解

    实现:

    在applicationContext-shiro.xml中开启shiro注解支持

    添加如下配置:

    <!--====开启Shiro的注解====--> <!-- Shiro注解借助了Spring的AOP来实现授权校验 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <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>

    2.开启Aop自动代理(已经完成)

    <!--6. 开启Aop自动代理--> <aop:aspectj-autoproxy/>

    3.在controller中使用@RequiresPermissions(“”)注解

    13. Shiro授权(六)Shiro标签实现授权验证

    编写test-auth.jsp

    在shiro-test.jsp中,通过shiro标签实现权限控制

    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>测试Shiro的jsp标签</title> </head> <body> <shiro:hasPermission name="企业管理"> <a href="">企业管理</a> </shiro:hasPermission> <hr/> <shiro:hasPermission name="用户管理"> <a href="">用户管理</a> </shiro:hasPermission> </body> </html>

    测试

    以老王(企业管理员)登录就只能看到用户管理,以张三(Saas管理员)登录就只能看到企业管理

    14. Shiro流程总结

    关键代码

    认证: subject.login(token); 授权: subject.checkPermission("");

    认证和授权流程

    15. 总结

    1)Shiro认证(重点)

    ​ 1.1 配置认证过滤器

    ​ 1.2 编写认证代码逻辑

    ​ 1.3 密码加密

    2)Shiro授权

    ​ 2.1 获取用户的权限

    ​ 2.2 掌握四种权限校验方法(XML配置,JSP标签)

    上课课件

    Processed: 0.038, SQL: 9