1.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.gzz</groupId>
<artifactId>20-spring-boot-shiro</artifactId>
<version>0.1</version>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2 配置类
package com.gzz.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro配置类
*/
@Configuration
public class ShiroConfig {
public static final int HASH_TIME = 32;
public static final String ALGORITHM_NAME = "MD5";
@Autowired
private UserRealm userRealm;
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
matcher.setHashAlgorithmName(ALGORITHM_NAME);// 指定加密方式为MD5
matcher.setHashIterations(HASH_TIME);// 加密次数
matcher.setStoredCredentialsHexEncoded(true);
userRealm.setCredentialsMatcher(matcher);
securityManager.setRealm(userRealm);// 关联realm.
bean.setSecurityManager(securityManager);// 设置 SecurityManager,安全管理器
bean.setLoginUrl("/index");// 设置登录跳转页面
bean.setUnauthorizedUrl("/unAuth");// 设置未授权提示页面
/**
* Shiro内置过滤器,可以实现拦截器相关的拦截器 常用的过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
**/
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/login", "anon");
filterMap.put("/user", "authc");
filterMap.put("/system", "perms[system]");
// filterMap.put("/static/**", "anon");
filterMap.put("/**", "authc");
filterMap.put("/logout", "logout");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
}
3.授权类
package com.gzz.config;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;
import com.gzz.sys.user.User;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("执行认证");
// 获取当前登录对象
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加当前用户的角色权限,用于判断可以访问那些功能
info.addStringPermissions(user.getPermissions());
info.addRoles(user.getRoles());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
// 编写shiro判断逻辑,判断用户名和密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
log.info("按登录名从数据库读取授权信息:授权信息到少应包括用户及所关联的角色集和权限集,至于表如何设计完全取决于项的实际需求与Shiro关系很小");
User user = DATABASE.get(token.getUsername());
if (user == null) {
throw new UnknownAccountException();
}
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getName());// 加盐也不是必选项,可以无此参数以账号作为加密的盐值
return new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, getName());
}
// 加密方法用于生成模拟数据:只有在保存用户密码到数据库时才会使用
public static void main(String[] args) {
System.out.println(new SimpleHash(ShiroConfig.ALGORITHM_NAME, "12345", ByteSource.Util.bytes("zhang"), ShiroConfig.HASH_TIME));
System.out.println(new SimpleHash(ShiroConfig.ALGORITHM_NAME, "12345", ByteSource.Util.bytes("li"), ShiroConfig.HASH_TIME));
}
private final static Map<String, User> DATABASE = Map.of(//
"zhang", new User("zhang", "b7d32295e4492a45d70bc34087890138", List.of("system", "test", "user"), Set.of("admin")), //
"li", new User("li", "138232801d3b1f5587c88d897fdc15bb", List.of(), Set.of("user"))//
);
}
4 测试案例
package com.gzz.sys.user;
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.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class UserController {
@GetMapping({ "/index", "/" })
public String index() {
return "login.html";
}
@RequestMapping(value = "/login")
public String login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
return "redirect:/main";
} catch (UnknownAccountException e) {
log.error("用户名不存在");
return "redirect:login.html";
} catch (IncorrectCredentialsException e) {
log.error("密码输入有误");
return "redirect:login.html";
}
}
@RequestMapping(value = "/main")
public String main() {
User user = (User) SecurityUtils.getSubject().getPrincipal();
log.info("用户:{}", user);
return "main.html";
}
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "redirect:/main.html";
}
@RequestMapping("/unAuth")
public String unAuth() {
return "unAuth.html";
}
@RequiresRoles("admin")
@RequestMapping("/system")
public String system() {
return "system.html";
}
@RequiresPermissions("user:save,update")
@RequestMapping("/user")
public String user() {
return "user.html";
}
}
5.User类
package com.gzz.sys.user;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class User {
// private Integer id;
private String name;
private String password;
private List<String> permissions;// 权限集
private Set<String> roles;// 角色集
}
6 演示
1674366018132.png
http://localhost:8080/
分别使用zhang li 密码12345登录 查看资源与日志信息使用理解
7 源码
[源码](https://gitee.com/gao_zhenzhong/spring-boots/tree/master/21-security/21-spring-boot-shiro)
网友评论