本文源码地址:https://github.com/jonssonyan/spring-demo/tree/master/springboot-shiro
在Spring Boot中集成Shiro进行用户的认证过程主要可以归纳为以下三点:
- 定义一个ShiroConfig,然后配置SecurityManager Bean,SecurityManager为Shiro的安全管理器,管理着所有Subject;
- 在ShiroConfig中配置ShiroFilterFactoryBean,其为Shiro过滤器工厂类,依赖于SecurityManager;
- 自定义Realm实现,Realm包含
doGetAuthorizationInfo()
和doGetAuthenticationInfo()
方法,因为本文只涉及用户认证,所以只实现doGetAuthenticationInfo()
方法。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.1</version>
</dependency>
ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultSecurityManager") DefaultSecurityManager defaultSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager);
// 添加shiro内置过滤器
/*
anon:无参,开放权限,可以理解为匿名用户或游客
authc:无参,需要认证
logout:无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url
authcBasic:无参,表示 httpBasic 认证
user:无参,表示必须存在用户,当登入操作时不做检查
ssl:无参,表示安全的URL请求,协议为 https
perms[user]:参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms["user, admin"],当有多个参数时必须每个参数都通过才算通过
roles[admin]:参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles["admin,user"],当有多个参数时必须每个参数都通过才算通过
rest[user]:根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等
port[8081]:当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数
*/
Map<String, String> map = new LinkedHashMap<>();
// 设置访问页面需要的权限
map.put("/user/add", "perms[user:add]");
map.put("/user/update", "perms[user:update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 设置转到登陆界面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 设置没有权限跳转的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager defaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
}
UserRealm
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置权限。这里只要登陆的用户都会执行下面这段代码,实际开发中,这里的权限应该从数据库中获取
info.addStringPermission("user:add");
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证");
// username,password从数据库中获取,这里为了方便在代码中写死
String username = "admin";
String password = "123";
// 前端传来的用户名密码从token中获取
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 用户名认证
if (!token.getUsername().equals(username)) {
return null; // 用户名不正确,抛出异常UnknownAccountException
}
// 密码认证,shiro做,加密了
return new SimpleAuthenticationInfo("", password, "");
}
}
Controller
@Controller
public class MyController {
@RequestMapping(value = {"/", "/index"})
public String login() {
return "login";
}
@RequestMapping("/user/add")
public String add() {
return "user/add";
}
@RequestMapping("/user/update")
public String update() {
return "user/update";
}
@RequestMapping("/toLogin")
public ModelAndView toLogin(@RequestParam String username, @RequestParam String password, ModelAndView modelAndView) {
// 获取当前用户subject
Subject subject = SecurityUtils.getSubject();
// 封装用户对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token); // 执行登陆操作
modelAndView.setViewName("index");
return modelAndView;
} catch (UnknownAccountException u) { // 捕获用户名是否正确的异常
modelAndView.addObject("msg", "用户名错误");
} catch (IncorrectCredentialsException i) { // 密码是否正确的异常
modelAndView.addObject("msg", "密码错误");
}
modelAndView.setViewName("login");
return modelAndView;
}
@RequestMapping("/noAuth")
@ResponseBody
public String noAuth() {
return "你尚未拥有权限";
}
}
前端页面
前端使用的thymeleaf模板引擎
login.html
登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
<form method="post" th:action="@{/toLogin}">
<input type="text" name="username" placeholder="请输入用户名">
<input type="password" name="password" placeholder="请输入密码">
<input type="submit" value="登陆">
</form>
</body>
</html>
index.html
后台主页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello shiro</h1>
<a th:href="@{user/add}">add</a>
<a th:href="@{user/update}">update</a>
</body>
</html>
add.html
添加页面,需要有添加权限
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>add</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
update.html
修改页面,需要有修改权限
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>update</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
测试
登录
后台页面
代码中值赋予user角色添加的权限,所以当我们点击add按钮时可用,点击update按钮时不可用