Spring Boot学习篇(十三)
Spring Boot学习篇(十三)
shiro安全框架使用篇(五)
1 准备工作
1.1 在SysUserMapper.xml中书写自定义标签
<select id="findRoles" resultType="string">select name from sys_role where id = (select roleid from sys_user_role where userid = (SELECT id FROM `sys_user` where username = #{username}))
</select>
<select id="findPerms" resultType="string">select name from sys_permission where id in(select perid from sys_role_permission where roleid in(select roleid from sys_user_role where userid in(SELECT id FROM `sys_user` where username =#{username})))
</select>
1.2 SysUserMapper接口书写自定义标签所对应的方法
//根据用户名查询角色
List<String> findRoles(String username);
//根据用户名查询权限
List<String> findPerms(String username);
1.3 login.html界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h2 th:if="${msg!=null}" th:text="${msg}"></h2><form action="/users/login" method="post"><!--需要与控制器保持一致-->用户名:<input type="text" name="yhm">密码:<input type="password" name="mm">记住我: <input type="checkbox" name="jzw"><button>登录</button></form>
</body>
</html>
1.4 index.html界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>主页<a href="/zhuxiao">注销</a><a href="/songs/find">查询商品</a><a href="/songs/update">更改商品</a><a href="/songs/insert">添加商品</a><a href="/songs/delete">删除商品</a>
</body>
</html>
1.5 qx.html界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>你没有该访问权限</h1><a href="/index.html">点击进入主页</a>
</body>
</html>
1.6 SongsController类
package com.zlz.controller;import com.zlz.entity.Songs;
import com.zlz.service.ISongsService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;/*** <p>* 前端控制器* </p>** @author zlz* @since 2023-02-04*/
//跨域的问题 1.协议不同 2.地址不同 3.端口不同 三者满足其一,就是跨域
//(origins = "*") origins仅允许指定域名访问 (origins = "http://127.0.0.1:8848")
@CrossOrigin
@Controller
@RequestMapping("/songs")
public class SongsController {@AutowiredISongsService iSongsService;@RequestMapping("select")@ResponseBodypublic List<Songs> s(){return iSongsService.list();}@RequestMapping("find")public String a(){System.out.println("查询数据");return "index";}@RequestMapping("delete")////角色,需要自定义异常@RequiresRoles(value = {"经理","组长"},logical = Logical.OR)//判断是否具有权限的
// @RequiresPermissions()public String b(){System.out.println("删除数据");return "index";}@RequiresUser@RequestMapping("update")public String c(){System.out.println("修改数据");return "index";}@RequestMapping("insert")public String d(){System.out.println("添加数据");return "index";}}
2.操作授权(全满足)
2.1 在MySqlRealm类里面(继承了AuthorizingRealm抽象类)书写授权方法的内容
@Resource
SysUserMapper sysUserMapper;
//授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) {//注意看方法的返回值,一般是返回方法返回值类型所对应的实现类(Simple开头+方法返回值类型)//①得到当前登录的用户名String yhm =(String)p.getPrimaryPrincipal();//②查询到该用户的角色和权限List<String> roles = sysUserMapper.findRoles(yhm);List<String> perms = sysUserMapper.findPerms(yhm);//③ 控制台打印 方便查看效果System.out.println(yhm+"具有权限"+roles);System.out.println(yhm+"具有角色"+perms);//④授权并返回SimpleAuthorizationInfo sm=new SimpleAuthorizationInfo();sm.addRoles(roles);sm.addStringPermissions(perms);return sm;
}
2.2 在ShiroConfig类的factoryBean方法中配置控制器过滤
//默认是roles[角色名1,角色名2...角色名n],因为底层用的是hasAllRoles方法,因此是必须含有全部权限
//下面代码表示经理和组长的权限都要满足才可以进行删除
map.put("/songs/delete","user,roles[经理,组长]");
2.3 在ShiroConfig类中的factoryBean方法配置没有权限时的跳转地址
//检测到没有权限时的地址
sb.setUnauthorizedUrl("/qx.html");
2.4 设置没有权限时跳转的页面(templates目录下)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>你没有该访问权限</h1><a href="/index.html">点击进入主页</a>
</body>
</html>
2.5 测试
2.5.1 使用admin用户(具有经理权限)登录后并点击删除商品进入如下界面
2.5.2 使用aaa用户(具有组长权限)登录后并点击删除商品进入如下界面
3.操作授权(满足其中之一)
3.1 自定义角色过滤器类MyRolesFilter(继承RolesAuthorizationFilter类)
package com.zlz.filter;import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.Set;/*** 自定义角色过滤器*/
public class MyRolesFilter extends RolesAuthorizationFilter {@Overridepublic boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {Subject subject = this.getSubject(request, response);String[] rolesArray = (String[])mappedValue;if (rolesArray != null && rolesArray.length != 0) {Set<String> roles = CollectionUtils.asSet(rolesArray);boolean b=false;for (String role : roles) {//满足其中之一就返回,hasRole满足条件就为true,if(subject.hasRole(role)){b=true;break;}}return b;} else {return true;}}
}
3.2 核心代码(在ShiroConfig类中factoryBean方法中)
/**
* 自定义过滤器 换个名字好点 sb是ShiroFilterFactoryBean类型的对象
*/
Map<String, Filter> myFilter=new HashMap<>();
myFilter.put("myroles",new MyRolesFilter());
sb.setFilters(myFilter);
//设置删除商品需要经理或者组长其一的权限
map.put("/songs/delete","user,myroles[经理,组长]");
3.3 完整的ShiroConfig类代码
package com.zlz.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.zlz.filter.MyRolesFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;@Configuration
public class ShiroConfig {//shiro标签库@Beanpublic ShiroDialect sd(){return new ShiroDialect();}//配置安全管理器 shiro核心配置类@Beanpublic DefaultWebSecurityManager securityManager(){DefaultWebSecurityManager dms=new DefaultWebSecurityManager();//设置域(查询数据库dao类) 查哪里的数据库设置dms.setRealm(realm());//设置会话管理器dms.setSessionManager(new DefaultWebSessionManager());//设置记住我管理器dms.setRememberMeManager(ck());return dms;}@Beanpublic Realm realm(){MysqlRealm r=new MysqlRealm();//设置密码加密 登录是加密比对HashedCredentialsMatcher hsm = new HashedCredentialsMatcher();hsm.setHashAlgorithmName("sha-256");hsm.setHashIterations(100);r.setCredentialsMatcher(hsm);return r;}//shiro过滤器(核心)@Bean("shiroFilterFactoryBean")public ShiroFilterFactoryBean factoryBean(){ShiroFilterFactoryBean sb=new ShiroFilterFactoryBean();//设置安全管理器sb.setSecurityManager(securityManager());/*基本设置*///检测到没有登录的地址sb.setLoginUrl("/login.html");//检测到没有权限时的地址(这里是一种设置没有权限时候跳转的地址)sb.setUnauthorizedUrl("/qx.html");/*** 自定义过滤器 换个名字好点 sb是ShiroFilterFactoryBean类型的对象是通过map集合设置进去的*/Map<String, Filter> myFilter=new HashMap<>();myFilter.put("myroles",new MyRolesFilter());sb.setFilters(myFilter);//控制器过滤设置 拦截有顺序///*** anon 允许匿名访问 无需登录* user 需要登录后才能访问* roles 需要具有所有的角色* perms 需要具有所有的权限* logout 注销过滤器* authc 不包含记住我 需要登录*/Map<String,String> map=new LinkedHashMap<>();map.put("/*.html", "anon");//注销流程 自己清空sessionmap.put("/zhuxiao", "logout");map.put("/songs/find","anon");//myroles就是和之前定义的key值保持一致map.put("/songs/delete","user,myroles[经理,组长]");//放行写上面 拦截写下面sb.setFilterChainDefinitionMap (map);return sb;}@Beanpublic CookieRememberMeManager ck(){CookieRememberMeManager c=new CookieRememberMeManager();SimpleCookie sc=new SimpleCookie();sc.setHttpOnly(true);//防止js读取cookiesc.setMaxAge(60);//设置cookie时长sc.setName("zlz");c.setCookie(sc);return c;}
}
3.3 测试
3.3.1 组长权限(用户名为aaa)
a 点击删除按钮后跳转的界面
b 点击删除按钮后的idea控制台界面
3.3.2 管理员权限(用户名为admin)
a 点击删除按钮后跳转的界面
b 点击删除按钮后的idea控制台界面
4 设置注销后跳转的地址
4.1 核心代码
//Map<String, Filter> myFilter=new HashMap<>();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/index.html");//修改注销后的跳转地址,如果没有设置默认跳转的是login.html界面myFilter.put("logout",logoutFilter);