30.0、spring-boot整合Shiro实现授权功能
在ShiroConfig.java文件里加上这段代码,完整代码往下滑可查看:
//授权,这利表示访问/user/add必须要有user:add权限才能访问
filterMap.put("/user/toadd","perms[user:add]");
filterMap.put("/user/toupdate","perms[user:update]");
//访问的页面如果未授权,跳转到哪个页面
bean.setUnauthorizedUrl("/unauthorized");
在MyController.java页面加入以下代码,完整代码往下滑查看:
@ResponseBody
@RequestMapping("/unauthorized")
public String unauthorized() {
return "该页面未授权无法访问";
}
为了能在UserRealm.java类中的授权方法里能够获取登录时用户的信息(包括权限等信息)
所以UserRealm.java类中的认证方法return new SimpleAuthenticationInfo()里,
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
的第一个参数写为user这样在授权方法里才可以拿到user对象里的信息。
然后用addStringPermission()方法设置当前用户的权限。
在UserRealm.java文件添加如下代码,完整代码下滑查看:
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
//拿到User对象
User currentuser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentuser.getPermit());
return info;
User.java文件如下:
package com.hkl.pojo;
public class User {
private int id;
private String username;
private String password;
private String permit;
public User(int id, String username, String password, String permit) {
this.id = id;
this.username = username;
this.password = password;
this.permit = permit;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPermit() {
return permit;
}
public void setPermit(String permit) {
this.permit = permit;
}
}
搭建一下环境:
创建一个add.html、update.html 文件,里面内容随便自定义即可
再创建一个login.html文件如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/logintoindex}" method="post">
<input type="text" name="username" placeholder="用户名" /><br>
<input type="password" name="password" placeholder="密码" /><br>
<input type="submit" value="登录" /><br>
</form>
</body>
</html>
创建 index.html 文件如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<hr>
<a th:href="@{/user/toadd}">进入添加用户界面</a>
<a th:href="@{/user/toupdate}">进入修改用户界面</a>
</body>
</html>
完整的UserConfig.java代码如下:
package com.hkl.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("getdefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设施安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon: 无需认证就可以访问
authc: 必须认证了才能访问
user: 必须拥有 记住我 功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//授权,这利表示访问/user/add必须要有user:add权限才能访问
filterMap.put("/user/toadd","perms[user:add]");
filterMap.put("/user/toupdate","perms[user:update]");
//这里表示所有在user下的访问都必须经过认证才可以
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置一下如果未授权,跳转到那个页面
bean.setUnauthorizedUrl("/unauthorized");
//设置登录页面的url请求
bean.setLoginUrl("/tologin");
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义类:1
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
}
UserRealm.java完整代码如下:
package com.hkl.config;
import com.hkl.pojo.User;
import com.hkl.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
//拿到User对象
User currentuser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentuser.getPermit());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthenticationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.queryUserByName(userToken.getUsername());
//如果用户名不正确
if(user == null) {//没有这个人
return null;//return null 的意思就是会抛出异常 UnknownAccountException 用户名不存在
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");//这里有三个对象,分别是获取当前用户的认证,密码,认证名,第一个和第三个可以先省略不懈
}
}
完整MyController.java代码如下:
package com.hkl.controller;
import com.hkl.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.websocket.server.PathParam;
@Controller
public class MyController {
@Autowired
private UserService userService;
@RequestMapping({"/","/index","index.html"})
public String toindex(Model model) {
model.addAttribute("msg","hello world首页");
return "index";
}
@RequestMapping("/user/toadd")
public String toadd() {
return "user/add";
}
@RequestMapping("/user/toupdate")
public String toupdate() {
return "user/update";
}
@RequestMapping("/tologin")
public String tologin() {
return "login";
}
@PostMapping("/logintoindex")
public String logintoindex(@PathParam("username") String username, @PathParam("password") String password,Model model) {
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
subject.login(token);//执行登录方法,如果没有异常就说明OK了
return "index";
}catch (UnknownAccountException e) {//用户名不存在
model.addAttribute("msg","用户名错误!");
return "login";
}catch (IncorrectCredentialsException e) {
model.addAttribute("msg","密码错误!");
return "login";
}
}
@ResponseBody
@RequestMapping("/unauthorized")
public String unauthorized() {
return "该页面未授权无法访问";
}
}
数据库如下:
首页和登录测试页面如下所示:
测试一下先登录小澜用户,然后进入首页点击访问一下add页面和update页面,因为小澜没有任何权限所以增加和修改页面应该都不能访问:
没有权限确实无法访问,然后跳转至该页面
然后来测试一下登录小张用户,进入首页后点击进入update页面确实可以进入,但是没有add权限所以不能进入add页面
有权限确实访问update页面成功了
最后来梳理一下:
我们在UserConfig.java文件中设置了只要访问/user/toadd的url,就会被拦然后执行UserRealm.java类里的授权方法,获取当前登录的用户的信息,获取当前用户的权限,然后设置权限,最后设置的权限会与需要的权限进行对比,如果权限不符合就跳转至unauthorization页面,符合权限就放行访问。
还是这个问题:
如果用户只有add的权限而没有update的权限,那么当这个用户登录进入首页页面的时候应该只显示add添加用户的按钮,而不显示update修改用户的按钮才对。
那么还是一样我们要实现这个功能就要是整合Thymeleaf
第一步:首先我们导入shiro和thymeleaf的整合相关依赖
<!--整合shiro和thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
第二步:在ShiroConfig.java文件中配置一下ShiroDialect这个Bean,完整代码下滑查看:
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
第三步:在index.html首页里:
<!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>
</head>
<body>
<h1>首页</h1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/tologin}">登录</a>
</div>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/toadd}">进入添加用户界面</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/toupdate}">进入修改用户界面</a>
</div>
</body>
</html>
第四步:在UserRealm.java文件中的认证方法里加入以下代码即可,完整代码下滑查看:
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
完整的UserRealm.java文件代码如下:
package com.hkl.config;
import com.hkl.pojo.User;
import com.hkl.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.security.Security;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
//SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
//拿到User对象
User currentuser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentuser.getPermit());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthenticationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.queryUserByName(userToken.getUsername());
//如果用户名不正确
if(user == null) {//没有这个人
return null;//return null 的意思就是会抛出异常 UnknownAccountException 用户名不存在
}
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
return new SimpleAuthenticationInfo(user,user.getPassword(),"");//这里有三个对象,分别是获取当前用户的认证,密码,认证名,第一个和第三个可以先省略不懈
}
}
测试一下:登录小澜用户进入首页确实没有显示任何按钮,因为他没有任何权限
但是这里【注意】,小澜在数据库中还是得给他随便设置一个权限,不然登陆过会报空指针异常
再测试登录小红:确实根据小红的权限,确实只显示了增加用户的按钮,而并没有显示他没有权限的修改用户按钮如下:
点击进入也确实能够访问add页面:
确实拥有add权限所以成功的访问了add页面