Commit 752e297f by wangzhen

first commit

0 parents
Showing with 1233 additions and 0 deletions
No preview for this file type
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
er_pm.png

31.9 KB

pm.png

104 KB

<?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>
<groupId>com.myxrk</groupId>
<artifactId>shiro-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>3.3.1</spring-boot.version>
<shiro-core.version>2.0.1</shiro-core.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</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-thymeleaf</artifactId>
</dependency>
<!-- shiro start -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
<version>${shiro-core.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>${shiro-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
<version>${shiro-core.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- shiro end -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.22.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.0-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring boot bom -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.2</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
## 基于JDBCRealm认证
1. **安全管理器:** DefaultWebSecurityManager
2. **认证授权器:** JDBCRealm
3. **Basic认证过滤器:** BasicHttpAuthenticationFilter
`BASIC_AUTH是HTTP基本认证的一种认证方式。当客户端发送HTTP请求时,可以在请求头中使用BASIC_AUTH方式进行身份认证。基本认证的工作原理是在HTTP请求头中发送用户名和密码的Base64编码,服务器端收到请求后解码并验证用户名和密码的合法性。`
4. **Token:** UsernamePasswordToken
5. **身份匹配器:** SimpleCredentialsMatcher
package com.myxrk.rbac;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.myxrk.rbac.dao")
@SpringBootApplication
@Slf4j
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class, args);
}
}
package com.myxrk.rbac.annotation;
import java.lang.annotation.*;
/**
* 数据表格绑定唯一标识注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataGrid {
/**
* 待绑定的数据表格的名称,需要与数据列权限配置的表格标识一致
*/
String value() default "";
}
package com.myxrk.rbac.aspect;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.CaseFormat;
import com.myxrk.rbac.annotation.DataGrid;
import com.myxrk.rbac.dao.SysDataGridMapper;
import com.myxrk.rbac.dao.SysRoleDataGridMapper;
import com.myxrk.rbac.dao.SysUserMapper;
import com.myxrk.rbac.dao.SysUserRoleMapper;
import com.myxrk.rbac.po.SysDataGrid;
import com.myxrk.rbac.po.SysRoleDataGrid;
import com.myxrk.rbac.po.SysUser;
import com.myxrk.rbac.po.SysUserRole;
import com.myxrk.rbac.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
@Aspect
@Component
public class DataGridMaskAspect {
private static final String MASK_STR = "---";
private static final String COLUMNS_ID = "id";
private final SysUserMapper sysUserMapper;
private final SysUserRoleMapper sysUserRoleMapper;
private final SysRoleDataGridMapper sysRoleDataGridMapper;
private final SysDataGridMapper sysDataGridMapper;
public DataGridMaskAspect(SysUserMapper sysUserMapper, SysUserRoleMapper sysUserRoleMapper, SysRoleDataGridMapper sysRoleDataGridMapper, SysDataGridMapper sysDataGridMapper) {
this.sysUserMapper = sysUserMapper;
this.sysUserRoleMapper = sysUserRoleMapper;
this.sysRoleDataGridMapper = sysRoleDataGridMapper;
this.sysDataGridMapper = sysDataGridMapper;
}
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public Object maskData(ProceedingJoinPoint joinPoint) throws Throwable {
// 执行目标方法
Object result = joinPoint.proceed();
// 仅处理Result类型,来源与ResponseBodyAdvice处理之后的结果
if (Result.class.isAssignableFrom(result.getClass())) {
List<?> data = ((Result<?>) result).getData();
if (data == null) {
return result;
}
Class<?> genericType = data.get(0).getClass();
// Type genericType= ((ParameterizedType)data.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if (genericType == String.class) {
return result;
}
// 获取数据表格唯一标识
Method tm = ((MethodSignature) joinPoint.getSignature()).getMethod();
DataGrid dg = tm.getDeclaredAnnotation(DataGrid.class);
if (dg == null) {
return result;
}
String dgKey = dg.value();
// 获取当前用户
SysUser sysUser = getSysUser();
// 获取当前激活角色,激活的角色只能有一个
Long role = getActiveRole(sysUser);
// 获取当前激活角色所关联的数据表格列
List<String> columns = getColunmsByRoleAndDataGridKey(dgKey, role);
maskData(data, columns, genericType);
return result;
}
// 不支持的返回类型, 直接放行
log.warn("Unsupported return type {}", result.getClass());
return result;
}
private static void maskData(List<?> data, List<String> columns, Class<?> genericType) {
// 获取返回值的属性列表
Set<String> header = getTableHeader(genericType);
if (!header.containsAll(columns)) {
log.error("backend data columns doesn't contains all data grid columns, please fix!: {}", columns);
return;
}
List<?> masked = data.stream().map(entry -> {
List<String> masks = getMasks(columns, header);
if (!masks.isEmpty()) {
masks.forEach(mask -> {
try {
String propertyName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, mask);
PropertyDescriptor pd = new PropertyDescriptor(propertyName, genericType);
if (pd.getWriteMethod() != null) {
// 获取 setter 方法
pd.getWriteMethod().invoke(entry, MASK_STR);
}
} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
return entry;
}).toList();
log.info("masked data: {}", masked);
}
private static List<String> getMasks(List<String> columns, Set<String> header) {
List<String> masks = new ArrayList<>(header);
masks.remove(COLUMNS_ID);
masks.removeAll(columns);
return masks;
}
private List<String> getColunmsByRoleAndDataGridKey(String dgKey, Long role) {
SysDataGrid sysDataGrid = sysDataGridMapper.selectOne(Wrappers.query(SysDataGrid.class).eq("dg_key", dgKey));
List<SysRoleDataGrid> sysRoleDataGrids = sysRoleDataGridMapper.selectList(Wrappers.query(SysRoleDataGrid.class).eq("role_id", role).eq("dg_id", sysDataGrid.getId()));
return sysRoleDataGrids.stream()
.flatMap(dataGrid -> Stream.of(dataGrid.getColumns().split(","))).toList();
}
private Long getActiveRole(SysUser sysUser) {
List<SysUserRole> sysUserRole = sysUserRoleMapper.selectList(Wrappers.query(SysUserRole.class).eq("user_id", sysUser.getUserId()));
Set<Long> roles = sysUserRole.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
return roles.iterator().next();
}
private SysUser getSysUser() {
// 获取当前用户
String username = (String) SecurityUtils.getSubject().getPrincipal();
return sysUserMapper.selectOne(Wrappers.query(SysUser.class).eq("username", username));
}
private static Set<String> getTableHeader(Class<?> genericType) {
Set<String> fields = new HashSet<>();
// 获取类的所有属性描述符
PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(genericType);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获取属性的 getter 方法
if (propertyDescriptor.getReadMethod() != null) {
String fieldName = propertyDescriptor.getName();
// 过滤掉 getClass() 方法
if (!fieldName.equals("class")) {
fields.add(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName));
}
}
}
return fields;
}
}
\ No newline at end of file
package com.myxrk.rbac.aspect;
import com.myxrk.rbac.exception.BaseException;
import com.myxrk.rbac.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class ErrorHandler {
@ExceptionHandler(Throwable.class)
public Result<Object> handleException(Exception e) {
log.error("Unknown Exception was thrown!", e);
return Result.error(500, e.getMessage());
}
@ExceptionHandler(BaseException.class)
public Result<Object> handleCustomException(BaseException e) {
log.warn("Biz Exception was thrown!", e);
return Result.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(AuthenticationException.class)
@ResponseBody
public Result<String> handleException(AuthenticationException e) {
log.warn("AuthenticationException was thrown!", e);
return Result.error(601, "授权失败");
}
@ExceptionHandler(AuthorizationException.class)
@ResponseBody
public Result<String> handleException(AuthorizationException e) {
log.warn("AuthorizationException was thrown!", e);
return Result.error(631, "授权失败");
}
}
\ No newline at end of file
package com.myxrk.rbac.aspect;
import com.myxrk.rbac.result.Result;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.Collections;
import java.util.List;
@ControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(@NonNull MethodParameter returnType, @NonNull Class converterType) {
// 对所有Controller的返回值进行拦截
return true;
}
@Override
public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType,
@NonNull Class selectedConverterType, @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {
// 如果已经是Result类型,直接返回
if (body instanceof Result) {
return body;
}
// 如果返回值是List类型,直接封装
if (body instanceof List) {
return Result.success((List<?>) body);
}
// 如果返回值不是List类型,将其封装为单元素的List
return Result.success(Collections.singletonList(body));
}
}
package com.myxrk.rbac.config;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.myxrk.rbac.dao.SysMenuMapper;
import com.myxrk.rbac.dao.SysRoleMenuMapper;
import com.myxrk.rbac.dao.SysUserMapper;
import com.myxrk.rbac.dao.SysUserRoleMapper;
import com.myxrk.rbac.exception.AccountException;
import com.myxrk.rbac.po.SysMenu;
import com.myxrk.rbac.po.SysRoleMenu;
import com.myxrk.rbac.po.SysUser;
import com.myxrk.rbac.po.SysUserRole;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class MySQLRealm extends AuthorizingRealm {
// @Inject
@Resource
private SysUserMapper sysUserMapper;
@Resource
private SysUserRoleMapper sysUserRoleMapper;
@Resource
private SysRoleMenuMapper sysRoleMenuMapper;
@Resource
private SysMenuMapper sysMenuMapper;
/**
* 用户、角色、权限
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
log.info("[username: {}] is authenticating", username);
// 0. get user by username
SysUser sysUser = sysUserMapper.selectOne(Wrappers.query(SysUser.class).eq("username", username));
// 1. 用户不存在
if (sysUser == null) {
throw new AccountException("用户名不正确");
}
// 2. 密码为空
String password = sysUser.getPassword();
if (null == password) {
throw new AccountException("用户名不正确");
}
// 3. 密码不正确
if (!password.equals(new String((char[]) token.getCredentials()))) {
throw new AccountException("密码不正确");
}
log.info("[username: {}] has been authenticated! info:{}", username, sysUser);
return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName());
}
/**
*
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) SecurityUtils.getSubject().getPrincipal();
log.info("[username: {}] is authorizing!", username);
// 0. get user by username
SysUser sysUser = sysUserMapper.selectOne(Wrappers.query(SysUser.class).eq("username", username));
// 1. roles
List<SysUserRole> sysUserRole = sysUserRoleMapper.selectList(Wrappers.query(SysUserRole.class).eq("user_id", sysUser.getUserId()));
Set<String> roles = sysUserRole.stream().map(SysUserRole::getRoleId).map(String::valueOf).collect(Collectors.toSet());
// 2. permissions
List<SysRoleMenu> sysRoleMenus = sysRoleMenuMapper.selectList(Wrappers.query(SysRoleMenu.class).in("role_id", roles));
List<Long> menus = sysRoleMenus.stream().map(SysRoleMenu::getMenuId).toList();
List<SysMenu> sysMenus = sysMenuMapper.selectList(Wrappers.query(SysMenu.class).in("menu_id", menus));
Set<String> permissions = sysMenus.stream().map(SysMenu::getPerms).collect(Collectors.toSet());
// 3. set roles and permissions
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(permissions);
log.info("[username: {}] has been authorized roles:{} and permissions:{}", username, roles, permissions);
return info;
}
}
package com.myxrk.rbac.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
private final DataSource dataSource;
public ShiroConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean("lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean("securityManager")
public SecurityManager securityManager(MySQLRealm mySQLRealm) {
return new DefaultWebSecurityManager(mySQLRealm);
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
/* 1. filter chain definition start */
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/favicon.ico", "anon");
filterMap.put("/shiro/anon", "anon");
filterMap.put("/captcha.jpg", "anon");
filterMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
/* filter chain definition end */
/* 2. filter chain start */
// shiroFilter.setFilters();
/* filter chain start */
/* 3. common url start */
shiroFilter.setLoginUrl("/auth/login");
shiroFilter.setSuccessUrl("/auth/index");
shiroFilter.setUnauthorizedUrl("/auth/401");
/* common url end */
return shiroFilter;
}
}
package com.myxrk.rbac.controller;
import com.myxrk.rbac.po.SysUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/auth")
@Controller
public class LoginController {
@GetMapping("/login")
public String login(Model model) {
model.addAttribute("greeting", new SysUser());
return "login";
}
@PostMapping("/login")
public String doLogin() {
return "redirect:/shiro/info";
}
@GetMapping("/index")
public String index() {
return "index";
}
@GetMapping("/401")
public String page401() {
return "401";
}
}
package com.myxrk.rbac.controller;
import com.myxrk.rbac.annotation.DataGrid;
import com.myxrk.rbac.dao.SysUserMapper;
import com.myxrk.rbac.po.Employee;
import com.myxrk.rbac.po.SysUser;
import com.myxrk.rbac.result.Result;
import com.myxrk.rbac.service.EmployeeService;
import com.myxrk.rbac.service.SysUserService;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/shiro")
public class ShiroController {
@Resource
private EmployeeService employeeService;
@Resource
private SysUserService sysUserService;
@GetMapping("/anon")
public Result<String> anon() {
return Result.success("不需要认证授权的url");
}
@GetMapping("/authentication")
public Result<String> authentication() {
return Result.success("认证成功");
}
@RequiresPermissions("sys:perm:read")
@GetMapping("/permRead")
public Result<String> permRead() {
return Result.success("授权:读");
}
@RequiresPermissions("sys:perm:write")
@GetMapping("/permWrite")
public Result<String> permWrite() {
return Result.success("授权:写");
}
@RequiresRoles("1")
@GetMapping("/roleSys")
public Result<String> roleSys() {
return Result.success("授权:系统管理员");
}
@RequiresRoles("2")
@GetMapping("/roleCom")
public Result<String> roleCom() {
return Result.success("授权:普通管理员");
}
//用户的信息
@GetMapping("/info")
public Result<String> info() {
Subject subject = SecurityUtils.getSubject();
Object principal = subject.getPrincipal();
boolean role1 = subject.hasRole("1");
boolean role2 = subject.hasRole("2");
boolean write = subject.isPermitted("sys:perm:write");
boolean read = subject.isPermitted("sys:perm:read");
String result = "[principal]:" + principal + " [write]:" + write + " [read]:" + read + " [role1]:" + role1 + " [role2]:" + role2;
return Result.success(result);
}
@DataGrid("dg_emp_list_001")
@GetMapping("/employees")
public Result<Employee> getEmployees() {
return Result.success(employeeService.getAllEmployees());
}
@DataGrid("dg_sys_user_list_001")
@GetMapping("/users")
public Result<SysUser> getUsers() {
return Result.success(sysUserService.getAllUsers());
}
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.Employee;
public interface EmployeeMapper extends BaseMapper<Employee> {
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysDataGrid;
public interface SysDataGridMapper extends BaseMapper<SysDataGrid> {
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysMenu;
public interface SysMenuMapper extends BaseMapper<SysMenu> {
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysRoleDataGrid;
public interface SysRoleDataGridMapper extends BaseMapper<SysRoleDataGrid> {
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysRoleMenu;
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysUser;
import java.util.List;
public interface SysUserMapper extends BaseMapper<SysUser> {
List<SysUser> getAllUsers();
}
package com.myxrk.rbac.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxrk.rbac.po.SysUserRole;
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
}
package com.myxrk.rbac.exception;
public class AccountException extends BaseException {
public AccountException(String message) {
super(10000, message);
}
}
package com.myxrk.rbac.exception;
import lombok.Data;
@Data
public class BaseException extends RuntimeException{
private int code;
private String message;
public BaseException(int code, String message) {
this.code = code;
this.message = message;
}
}
package com.myxrk.rbac.po;
import lombok.Data;
@Data
public class Employee {
private Long id;
private String name;
private String salary;
private String address;
}
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_dg")
public class SysDataGrid {
private Long id;
private String dgKey;
private String columns;
}
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_menu")
public class SysMenu {
private Long menuId;
private Long parentId;
private String name;
private String url;
private String perms;
private Integer type;
private String icon;
private Integer orderNum;
}
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_role_dg")
public class SysRoleDataGrid {
private Long id;
private Long roleId;
private Long dgId;
private String columns;
}
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("sys_role_menu")
@Data
public class SysRoleMenu {
private Long id;
private Long roleId;
private Long menuId;
}
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@TableName("sys_user")
@Data
public class SysUser {
private Long userId;
private String username;
private String name;
private String password;
private String salt;
private String email;
private String mobile;
private Integer status;
private String provinceCode;
private String department;
private String createBy;
private Date createTime;
private String updatedBy;
private Date updatedTime;
}
\ No newline at end of file
package com.myxrk.rbac.po;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("sys_user_role")
@Data
public class SysUserRole {
private Long id;
private Long userId;
private Long roleId;
}
package com.myxrk.rbac.result;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.ToString;
import java.util.Collections;
import java.util.List;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ToString
@Data
public class Result<T> {
private int code;
private String message;
private List<T> data;
public Result(int code, String message, List<T> data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <U> Result<U> error(int code, U msg) {
if (msg == null) {
return new Result<>(code, "", null);
}
if (msg instanceof String msgStr) {
return new Result<>(code, msgStr, null);
}
return new Result<>(code, String.valueOf(msg), null);
}
public static <T> Result<T> success(T data) {
return new Result<>(0, "Okay", Collections.singletonList(data));
}
public static <T> Result<T> success(List<T> data) {
return new Result<>(0, "Okay", data);
}
}
package com.myxrk.rbac.service;
import com.myxrk.rbac.dao.EmployeeMapper;
import com.myxrk.rbac.po.Employee;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmployeeService {
@Resource
private EmployeeMapper employeeMapper;
public List<Employee> getAllEmployees() {
return employeeMapper.selectList(null);
}
}
\ No newline at end of file
package com.myxrk.rbac.service;
import com.myxrk.rbac.dao.SysUserMapper;
import com.myxrk.rbac.po.SysUser;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysUserService {
@Resource
private SysUserMapper sysUserMapper;
public List<SysUser> getAllUsers() {
return sysUserMapper.selectList(null);
}
}
No preview for this file type
server:
port: 8089
spring:
datasource:
type: com.mysql.cj.jdbc.MysqlDataSource
username: root
password:
url: jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
mybatis-plus:
global-config:
banner: off
logging:
level:
org.apache.shiro.authc.AbstractAuthenticator: debug
-- CREATE USER 'root'@'%' IDENTIFIED BY '';
-- CREATE DATABASE IF NOT EXISTS shiro DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
-- grant all privileges on shiro.* to 'root'@'%';
-- flush privileges;
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`
(
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '登录用户名',
`name` varchar(128) DEFAULT NULL COMMENT '姓名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(20) DEFAULT NULL COMMENT '盐',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
`status` tinyint DEFAULT '1' COMMENT '状态 0:已注销 1: 正常',
`province_code` varchar(32) DEFAULT NULL COMMENT '所属省code码',
`department` varchar(200) DEFAULT NULL COMMENT '部门',
`create_by` varchar(50) DEFAULT 'system' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_by` varchar(50) DEFAULT 'system' COMMENT '更新人',
`updated_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`user_id`) USING BTREE,
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB COMMENT='用户表';
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`
(
`role_id` bigint NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) DEFAULT NULL COMMENT '角色名称',
`remark` varchar(100) DEFAULT NULL COMMENT '备注',
`create_user_id` bigint DEFAULT NULL COMMENT '创建者ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE=InnoDB COMMENT='角色';
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT '用户ID',
`role_id` bigint DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='用户与角色对应关系';
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu`
(
`menu_id` bigint unsigned NOT NULL AUTO_INCREMENT,
`parent_id` bigint DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`name` varchar(50) DEFAULT NULL COMMENT '菜单名称',
`url` varchar(1000) DEFAULT NULL COMMENT '菜单URL',
`perms` varchar(500) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
`type` int DEFAULT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',
`icon` varchar(50) DEFAULT NULL COMMENT '菜单图标',
`order_num` int DEFAULT NULL COMMENT '排序',
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE=InnoDB COMMENT='菜单';
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu`
(
`id` bigint NOT NULL AUTO_INCREMENT,
`role_id` bigint DEFAULT NULL COMMENT '角色ID',
`menu_id` bigint DEFAULT NULL COMMENT '菜单ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='角色与菜单对应关系';
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
`salary` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '薪水',
`address` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='雇员表';
INSERT INTO `sys_user`
VALUES (1, 'test001', 'test001', 'test001', NULL, NULL, NULL, 1, NULL, NULL, 'system', '2024-08-24 10:19:57', 'system',
'2023-12-22 10:19:57'),
(2, 'test002', 'test002', 'test002', NULL, NULL, NULL, 1, NULL, NULL, 'system', '2024-08-24 10:19:57', 'system',
'2023-12-22 10:19:57');
INSERT INTO `sys_user_role`
VALUES (1, 1, 1),
(2, 2, 2);
INSERT INTO `sys_role`
VALUES (1, '系统管理员', '系统管理员', NULL, '2023-12-29 18:03:52'),
(2, '普通管理员', '普通管理员', NULL, '2023-12-29 18:03:52');
INSERT INTO `sys_role_menu`
VALUES (1, 1, 1),
(2, 1, 2),
(3, 2, 1),
(4, 2, 3),
(5, 1, 4);
INSERT INTO `sys_menu`
VALUES (1, null, '', null, 'sys:perm:read', null, null, null),
(2, null, '', null, 'sys:perm:write', null, null, null),
(3, null, '', null, 'employee:view:name,address', null, null, null),
(4, null, '', null, 'employee:view', null, null, null);
INSERT INTO `employee`
VALUES (1, 'Alice', '5000', '123 Main St'),
(2, 'Bob', '6000', '456 Oak St');
No preview for this file type
401
\ No newline at end of file
404
\ No newline at end of file
INDEX
\ No newline at end of file
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>User Registration</title>
</head>
<body>
<form action="#" th:action="@{/auth/login}" th:object="${greeting}" method="post">
<p>Username: <input type="text" th:field="*{username}"/></p>
<p>Password: <input type="password" th:field="*{password}"/></p>
<p><input type="submit" value="Login"/></p>
</form>
</body>
</html>
\ No newline at end of file
package com.myxrk.rbac;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Base64;
@SpringBootTest
class ShiroApplicationTests {
@Test
void passwordBase64() {
String password = "shiro@kuihai";
byte[] passwordBytes = password.getBytes();
String passwordBase64 = Base64.getEncoder().encodeToString(passwordBytes);
passwordBytes = Base64.getDecoder().decode(passwordBase64);
password = new String(passwordBytes);
System.out.println("password=>" + password);
System.out.println("passwordBase64=>" + passwordBase64);
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!