SpringBoot集成Shiro权限框架
引入Shiro相关依赖
在项目中添加Shiro核心组件,包括认证、Web支持及与Spring的集成模块:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
配置Shiro安全中心
通过Java配置类定义安全管理器和过滤器链,实现登录拦截与权限控制:
@Configuration
public class SecurityConfig {
@Bean
public WebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm());
return manager;
}
@Bean
public ShiroFilterFactoryBean filterChain() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login/page");
bean.setSuccessUrl("/dashboard");
bean.setUnauthorizedUrl("/error/403");
Map<String, String> filterMap = new HashMap<>();
filterMap.put("/login/**", "anon");
filterMap.put("/static/**", "anon");
filterMap.put("/api/**", "authc");
filterMap.put("/**", "authc");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
@Bean
public DefaultAdvisorAutoProxyCreator proxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
@Bean
public AuthorizationAttributeSourceAdvisor advisor() {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager());
return advisor;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
public MyRealm customRealm() {
return new MyRealm();
}
}
自定义认证与授权域
实现用户身份验证与权限分配逻辑,基于用户名密码进行校验,并动态加载权限信息:
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
UsernamePasswordToken auth = (UsernamePasswordToken) token;
String username = auth.getUsername();
User user = null;
if ("admin".equals(username)) {
user = new User("admin", "admin");
} else if ("user".equals(username)) {
user = new User("user", "user");
}
if (user == null) {
throw new UnknownAccountException("用户不存在");
}
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
Set<String> permissions = new HashSet<>();
if ("admin".equals(username)) {
permissions.add("data:read");
permissions.add("data:write");
permissions.add("system:manage");
} else if ("user".equals(username)) {
permissions.add("data:read");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
return info;
}
}
控制器处理请求
提供登录、登出及受保护资源访问接口,结合Subject进行会话管理:
@Controller
public class AuthController {
@GetMapping("/login/page")
public String showLoginPage() {
return "login";
}
@PostMapping("/login")
public String authenticate(@ModelAttribute User user, Map<String, Object> model) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (IncorrectCredentialsException e) {
model.put("error", "密码错误,请重试");
return "login";
} catch (UnknownAccountException e) {
model.put("error", "用户不存在");
return "login";
}
return "redirect:/dashboard";
}
@GetMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login/page";
}
@RequiresPermissions("data:read")
@GetMapping("/data/view")
public String viewData() {
return "data";
}
@RequiresPermissions("data:write")
@GetMapping("/data/edit")
public String editData() {
return "edit";
}
@GetMapping("/error/403")
public String forbidden() {
return "error/403";
}
}
全局异常处理
捕获权限不足异常并统一跳转至错误页面:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AuthorizationException.class)
public String handleForbidden(Exception ex, HttpServletRequest request) {
request.setAttribute("javax.servlet.error.status_code", 403);
return "forward:/error/403";
}
}
集成Thymeleaf模板权限标签
启用前端页面的权限判断功能,实现条件渲染。
- 添加依赖:
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> - 配置文件中已注册Shiro方言(见上文)
- HTML头部声明命名空间:
<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> - 使用权限标签控制元素显示:
<div shiro:hasPermission="data:read"> <a href="/data/view">查看数据</a> </div> <div shiro:hasPermission="data:write"> <a href="/data/edit">编辑数据</a> </div>