20.1 Shiro授权的实现
- 先写一个 未授权的 Controller,然后设置 没有授权会跳转的页面
@RequestMapping("noauth")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问此页面";
}
// perms[user:add] 必须得有 user:add 权限才可以 访问 /user/add
filterMap.put("/user/add","perms[user:add]");
// 未授权 会跳转的页面
bean.setUnauthorizedUrl("/noauth");
filterMap.put("/user/add","perms[user:add]");
:这个其实 就是 授权 最常用的监测点。就是通过授权 来判断你是否 可以访问页面!
- 在授权方法里面 赋予授权
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权");
// 授予用户 权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermission("user:add");
return simpleAuthorizationInfo;
}
这样的话,你会发现 无论你登录 哪个 用户 都会 去 赋予 授权。诶?这样不就 完蛋了吗?我们 必须 根据 用户 来 赋予给它 对应的授权呀。
- 数据库设计 perms 字段、role 字段 来 判断赋予的授权!
首先 我们 肯定 要解决最大的难题,就是 授权那里,怎么能拿到 当前认证登录成功的用户呢??因为如果你拿不到。那肯定 是 无法 取出 对应的 perms
和 role
的呀。
- 解决方案:
分析 SimpleAuthenticationInfo(Principal,user.getPwd(),realmName);
我们发现 第一个参数 是 信息主体,也就是 一个 对象。那么 我们想一下,认证登录的时候,什么玩意 是 信息的主体?答: user 对象
,整个user 对象 就是 我们 需要的信息主体。
所以我们 直接 传递 过去 就可以了。
// 认证方法那里
return new SimpleAuthenticationInfo(user,user.getPwd(),"userRealm");
然后 我们知道 SecurityUtils.getSubject(); 获取到的 subject 是独立的。所以 在授权方法那里 也是可以拿到的。
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权");
// 拿到 当前 登录的这个对象
Subject subject = SecurityUtils.getSubject();
// 拿到 信息主体 user 这个对象,因为我们 传递 就是 user 对象
User user = (User)subject.getPrincipal();
// 授予用户 权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 添加 用户的 权限
simpleAuthorizationInfo.addStringPermission(user.getPerms());
// 添加 用户的 角色
simpleAuthorizationInfo.addRole(user.getRole());
return simpleAuthorizationInfo;
}
我们正常开发的话,权限和角色不可能是一个。而一般情况下,我们都会把多个权限和角色的字符串拼接在一起。比如说 权限A|权限B 这样的方式,然后 用 split() 分割成 一个 String 的数组即可。
20.2 整合 Thymeleaf
其实 Shiro 整合 Thymeleaf,就是用来 做 授权控制页面渲染的
。
- 导入 依赖
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
- 整合 shiroDialect
// 整合 shiroDialect,为了 thymeleaf 做的
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
3. 使用 命名空间 shiro:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p>[[${msg}]]</p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
<a th:href="@{/user/logout}">注销</a>
</body>
</html>
- 利用 subjec.getSession() 去编写
session
因为我们 是 DefaultWebSessionManager
也就是 WEB 项目的 管理者,所以 此时 获取到的 session 就是 httpSession。这个 不用怀疑。
我们 直接可以 在 认证 这里 进行 session 节点的添加。
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.getUserByName(token.getUsername());
if(user == null){
return null;// 这样的写法 会自动 抛出 异常 UnknowAccountException
}
// 设置一个 session 的键值对节点
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser",user);
// 密码认证,shiro 不让我们 开发者去做。它帮我们做。所以不用写
return new SimpleAuthenticationInfo(user,user.getPwd(),"userRealm");
}
subject.logout(); // 会自动 销毁 session
让其 没有 登陆过的,显示 登录,不显示注销。让其登陆过的,只显示 注销。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p>[[${msg}]]</p>
<hr>
<div th:if="${session.loginUser == null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
<div th:if="!${session.loginUser == null}">
<a th:href="@{/user/logout}">注销</a>
</div>
</body>
</html>