学习shiro安全框架,顺便做个笔记。声明一下,这只是一个学习过程中的笔记。该案例是一个结合SpringBoot搭建的shiro小案例。
第一步:
先导入要用到的依赖,这里导入的是shiro与spring整合的依赖包。
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.9.1</version>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
第二步:
需要一个Realm,可以自定义一个XXXRealm类去继承AuthorizingRealm,同时需要实现doGetAuthorizationInfo方法和doGetAuthenticationInfo方法。在doGetAuthorizationInfo方法里可以进行授权操作,doGetAuthenticationInfo方法里可进行认证操作。
/**
* 自定义Realm类,继承AuthorizingRealm
*/
public class UserRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
List<String> permissionList = new ArrayList<>();
// 权限可以从数据库里查
permissionList.add("permission:select");
permissionList.add("permission:insert");
permissionList.add("permission:delete");
permissionList.add("permission:update");
/*
addStringPermission(String) 添加一个权限
addStringPermissions(Collection<String>) 添加多个权限
addRole(String) 添加一个角色
addRoles(Collection<String>) 添加多个角色
*/
// 获取当前用户
// Object principal = SecurityUtils.getSubject().getPrincipal();
// 完成授权
authorizationInfo.addStringPermissions(permissionList);
return authorizationInfo;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证");
// 用户名和密码可以从数据库中查
String username = "zhangsan";
String password = "123456";
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if (!username.equals(token.getUsername())) {
return null;
}
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(token,password,"");
return authenticationInfo;
}
}
以下是一个shiro的配置类
/**
* Configuration class
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 一定要配置SecurityManager,否则报错
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
/*
anon:无需认证即可访问
authc:必须认证后才能访问
perms:需要有指定权限才能访问
role:需要有指定角色才能访问
*/
// p1页面需要拥有 permission:select 权限才能访问
filterChainDefinitionMap.put("/p1","perms[permission:select]");
// p2页面需要拥有 permission:insert 权限才能访问
filterChainDefinitionMap.put("/p2","perms[permission:insert]");
// /(首页)需要认证才能访问
filterChainDefinitionMap.put("/","authc");
// 登录页面可以直接访问
filterChainDefinitionMap.put("/login","anon");
// 出以上的请求,其他的可以直接访问
filterChainDefinitionMap.put("/**","anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 配置登录页
shiroFilterFactoryBean.setLoginUrl("/login");
// 配置无权限的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
return shiroFilterFactoryBean;
}
/**
* SecurityManager
* @param realm
* @return
*/
@Bean
public SecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* Realm
* @return
*/
@Bean
public Realm realm() {
return new UserRealm();
}
}
以下是Controller
@Controller
public class ShiroController {
@RequestMapping("/")
public String index() {
return "index";
}
@RequestMapping("/login")
public String toLogin() {
return "login";
}
@RequestMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
// 退出后回到登录页
return "login";
}
@RequestMapping("/signIn")
public String login(String username,String password,Model model) {
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
try {
subject.login(usernamePasswordToken);
// 认证完成后跳转到首页
return "index";
} catch (UnknownAccountException e) {
// UnknownAccountException 用户名不正确
e.printStackTrace();
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {
// IncorrectCredentialsException 密码错误
e.printStackTrace();
model.addAttribute("msg","密码错误");
return "login";
} catch (AuthenticationException e) {
e.printStackTrace();
return "login";
}
}
@RequestMapping("/unauthorized")
public String unauthorized() {
// 跳到无权限的提示页面
return "unauthorized";
}
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "hello";
}
@RequestMapping("/p1")
public String p1(Model model) {
model.addAttribute("msg","Welcome to page p1");
return "pages/p1";
}
@RequestMapping("/p2")
public String p2(Model model) {
model.addAttribute("msg","Welcome to page p2");
return "pages/p2";
}
}
为了更直观的看到测试结果,写了几个非常简陋的HTML页面,下图是几个页面的存放位置
1、index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<h3><a href="/p1">p1</a></h3>
<h3><a href="/p2">p2</a></h3>
<h3><a href="/logout">退出登录</a></h3>
</body>
</html>
2、login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro login page</h1>
<form action="/signIn">
username:<input type="text" name="username" /><br />
password:<input type="password" name="password" /><br />
<input type="submit" value="login" />
</form>
<span th:text="${msg}" style="color: red;font-size: 16px;"></span>
</body>
</html>
3、unauthorized.html (无权限时跳转的页面)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro login page</h1>
<form action="/signIn">
username:<input type="text" name="username" /><br />
password:<input type="password" name="password" /><br />
<input type="submit" value="login" />
</form>
<span th:text="${msg}" style="color: red;font-size: 16px;"></span>
</body>
</html>
4、p1.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>p1's page</h1>
<h3 th:text="${msg}"></h3>
</body>
</html>
5、p2.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>p2's page</h1>
<h3 th:text="${msg}"></h3>
</body>
</html>