引言
本文主要实现springboot项目整合shiro实现简单的权限验证,包括用户登录认证,不同的角色访问受权限管控的接口,不同权限的用户访问受不同权限管控的接口等操作,数据库表包含user(用户表)、role(角色表)、perm(权限表)、user_role_relation(用户角色对应关系表)、role_perm_relation(角色权限对应关系表)。其中用户可以有多个角色,单个角色也可以有多个权限。
一、引入依赖
yaml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.3</version>
</dependency>
二、自定义realm
java
public class myRealm extends AuthorizingRealm {
@Autowired
private RoleService roleService;
@Autowired
private PermsService permsService;
@Autowired
private UserService userService;
// 实现对用户的授权功能,通过用户登录的信息查找到用户所有的角色以及角色对应的权限集合,并将用户的角色以及权限集合设置到AuthorizationInfo对象中返回,
// 可以用于后续接口(Controller)中通过 @RequiresRoles("admin") 注解限制特定角色访问该接口,@RequiresPermissions("perm:delete") 注解限制有指定权限的用户访问接口
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Subject subject = SecurityUtils.getSubject();
User principal = (User)subject.getPrincipal();
Set<String> roleSet = roleService.getRoleNameByUserId(principal.getId());
Set<String> permSet = permsService.getPermByUserId(principal.getId());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roleSet);
info.setStringPermissions(permSet);
return info;
}
// 实现对用户登录认证的功能,也就是实现subject.login(usernamePasswordToken)对应的功能,这里的参数authenticationToken和usernamePasswordToken相对应
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
User user = userService.lambdaQuery().eq(User::getUsername,token.getUsername()).one();
if(user!=null){
return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
}
return null;
}
}
三、创建shiro配置类
java
@Configuration
public class ShiroConfig {
// 将自定义的myRealm实现类交由spring容器管理,并设置加密规则
@Bean
public myRealm getRealm(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1024);
myRealm realm = new myRealm();
realm.setCredentialsMatcher(matcher);
return realm;
}
// spring自动将容器中的myRealm类型的bean注入到对应参数中,无需通过 @Qualifier指定
@Bean
public DefaultWebSecurityManager getSecurityManager(myRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
// 可以在这里设置过滤规则
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 以下两个bean是开启注解方式控制访问url
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
四、接口的编写
UserController控制器
java
@RestController
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserRoleRelationService userRoleRelationService;
@PostMapping("/register")
public R register(@RequestBody User user){
Md5Hash md5Hash = new Md5Hash(user.getPassword(),null,1024);
user.setPassword(md5Hash.toHex());
if(userService.save(user)){
userRoleRelationService.batchInsert(user.getId(),user.getRoleIdList());
return R.ok().message("用户注册成功");
}else {
return R.error().message("用户注册失败");
}
}
@PostMapping("/login")
public R login(@RequestBody User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try{
subject.login(usernamePasswordToken);
User u = (User)subject.getPrincipal();
subject.getSession().setAttribute("user",u);
}catch (UnknownAccountException e){
return R.error().message("用户名错误");
}catch (IncorrectCredentialsException e){
return R.error().message("密码错误");
}
return R.ok().message("登录成功");
}
@GetMapping("/logout")
public R logout(){
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
subject.logout();
return R.ok().message("当前用户"+currentUser.getUsername()+"退出登录");
}
@GetMapping("/getCurrentUser")
public R getCurrentUser(){
Subject subject = SecurityUtils.getSubject();
User principal = (User)subject.getPrincipal();
return R.ok().data("currentUser",principal);
}
}
PermController控制器
java
@RestController
public class PermController {
@Autowired
private PermsService permsService;
@PostMapping("/addPerm")
@RequiresRoles("admin")
public R addPerm(@RequestBody Perms perms){
if(permsService.save(perms)){
return R.ok().message("新增权限成功");
}else {
return R.error().message("新增权限失败");
}
}
@GetMapping("/deletePerm")
@RequiresPermissions("perm:delete")
public R deletePerm(Integer id){
if(permsService.removeById(id)){
return R.ok().message("删除成功");
}else {
return R.error().message("删除失败");
}
}
@PostMapping("/empower")
@RequiresRoles("admin")
public R empower(@RequestBody Perms perms){
if(permsService.batchInsert(perms)){
return R.ok().message("对当前角色授权成功");
}else {
return R.error().message("授权出现异常");
}
}
}