springboot整合shiro
springboot整合shiro
Shiro核心组件
1、UsernamePasswordToken,Shiro 用来封装用户登录信息,使用用户的登录信息创建令牌 Token,登录的过程即 Shiro 验证令牌是否具有合法身份以及相关权限。
2、 SecurityManager,Shiro 的核心部分,负责安全认证与授权。
3、Subject,Shiro 的一个抽象概念,包含了用户信息。
4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑在 Realm 中实现。
5、AuthenticationInfo,用户的角色信息集合,认证时使用。
6、AuthorizationInfo,角色的权限信息集合,授权时使用。
7、DefaultWebSecurityManager,安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。
8、ShiroFilterFactoryBean,过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建一个个 Filter 对象来完成。
引入Shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
自定义 Shiro 完成用户认证,在 MyRealm 中完成代码的编写
public class MyRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Account account = accountService.findByUsername(token.getUsername());
if(account != null){
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
return null;
}
}
客户端传来的 username 和 password 会自动封装到 token,先根据 username 进行查询,如果返回 null,则表示用户名错误,直接 return null 即可,Shiro 会自动抛出 UnknownAccountException 异常。
如果返回不为 null,则表示用户名正确,再验证密码,直接返回 SimpleAuthenticationInfo 对象即可,如果密码验证成功,Shiro 认证通过,否则返回 IncorrectCredentialsException 异常。
自定义过滤器创建完成之后,需要进行配置才能生效,在 Spring Boot 应用中,不需要任何的 XML 配置,直接通过配置类进行装配,代码如下所示
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager);
return factoryBean;
}
@Bean
public DefaultWebSecurityManager manager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
return manager;
}
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
}
这个配置类中一共自动装配了 3 个 bean 实例,第一个是自定义过滤器 MyRealm,我们的业务逻辑全部定义在这个 bean 中。
然后需要创建第二个 bean 示例 DefaultWebSecurityManager,并且将 MyRealm 注入到 DefaultWebSecurityManager bean 中,完成注册。
最终需要装配第三个 bean ShiroFilterFactoryBean,这是 Shiro 自带的一个 Filter 工厂实例,所有的认证和授权判断都是由这个 bean 生成的 Filter 对象来完成的,这就是 Shiro 框架的运行机制,开发者只需要定义规则,进行配置,具体的执行者全部由 Shiro 自己创建的 Filter 来完成。
所以我们需要给 ShiroFilterFactoryBean 实例注入认证及授权规则,如下所示
认证过滤器:
anon:无需认证即可访问,游客身份。
authc:必须认证(登录)才能访问。
authcBasic:需要通过 httpBasic 认证。
user:不一定已通过认证,只要是曾经被 Shiro 记住过登录状态的用户就可以正常发起请求,比如 rememberMe。授权过滤器:
perms:必须拥有对某个资源的访问权限(授权)才能访问。
role:必须拥有某个角色权限才能访问。
port:请求的端口必须为指定值才可以访问。
rest:请求必须是 RESTful,method 为 post、get、delete、put。
ssl:必须是安全的 URL 请求,协议为 HTTPS。
比如,我们创建三个页面,main.html、manage.html、administrator.html,要求如下:
1、必须是登录状态才可以访问 main.html。
2、用户必须拥有 manage 授权才可以访问 manage.html。
3、用户必须拥有 administrator 角色才能访问 administrator.html。
代码如下所示。
@Bean
public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager);
Map<String,String> map = new HashMap<>();
map.put("/main","authc");
map.put("/manage","perms[manage]");
map.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
shiro-thymeleaf整合
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro内置过滤器
Map<String, String> filterMap = new LinkedHashMap<>();
//user 用户,add 权限可访问
filterMap.put("/user/add","perms[user:add]"); //默认跳转未授权
filterMap.put("/user/del","perms[user:del]");
filterMap.put("/user/*","authc"); //需要认证才能访问
factoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录请求
factoryBean.setLoginUrl("/doLogin");
// 设置未授权
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联 realm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect shiro-thymeleaf整合
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
USerRealm
//自定义UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了->授权doGetAuthorizationInfo");
//拿到当前登录的用户
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//用户授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//伪造数据 数据库中取
String name="root";
String password="123456";
System.out.println("执行了->认证doGetAuthenticationInfo");
UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
User user = userService.findUserByName(userToken.getUsername());
System.out.println("user--->"+user);
//用户名不存在
if (user==null)
return null;
// if (!userToken.getUsername().equals(name)){
// return null; //抛出异常
// }
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
//密码认证 Shiro做 传递用户
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
Controller
@Controller
public class ShiroController {
@RequestMapping({"/","/index"})
public String test1(Model model){
model.addAttribute("msg","Hello,Shiro");
return "index";
}
@RequestMapping("/user/add")
public String addUser(){
return "user/add";
}
@RequestMapping("/user/del")
public String delUser(){
return "user/del";
}
@RequestMapping("/user/select")
public String selectUser(){
return "user/select";
}
@RequestMapping("/doLogin")
public String doLogin(){
return "login";
}
@GetMapping("/login")
public String login(String username,String password,Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
e.printStackTrace();
model.addAttribute("message","用户名错误!");
return "login";
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
model.addAttribute("message","密码错误!");
return "login";
}
}
@RequestMapping("/unauth")
@ResponseBody
public String unauth(){
return "未经授权,暂无法访问!";
}
}
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<div>
<form action="index.html" th:action="@{/login}" method="get">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<span th:text="${message}" style="color: red"></span>
<p><input type="submit"></p>
</form>
</div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<!--用户登录隐藏按钮-->
<div th:if="${session.loginUser==null}">
<a th:href="@{/doLogin}">登录</a>
</div>
<p th:text="${msg}">hello</p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a> <br>
</div>
<div shiro:hasPermission="user:del">
<a th:href="@{/user/del}">del</a> <br>
</div>
<a th:href="@{/user/select}">select</a> <br>
</body>
</html>
add del select.html
添加用户
删除用户
浙公网安备 33010602011771号