springboot整合shiro进行权限管理
Shiro大家应该都了解过,话不多说,直接上重点
源码:https://github.com/ROAOR1/shiro
数据库设计
为了方便,只创建了三张表,用户表,权限表,用户权限表
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`phone` varchar(11) NOT NULL,
`status` tinyint(2) DEFAULT NULL,
`salt` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '张三', '09c74ada9e6c9fbdef249b258a66e948', '13564156412', '1', 'ABC');
INSERT INTO `user` VALUES ('2', '李四', '09c74ada9e6c9fbdef249b258a66e948', '13564156413', '1', 'ABC');
INSERT INTO `user` VALUES ('3', '王五', '09c74ada9e6c9fbdef249b258a66e948', '13564156411', '1', 'ABC');
CREATE TABLE `perm` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `perm` VALUES ('1', '查询权限');
INSERT INTO `perm` VALUES ('2', '添加权限');
INSERT INTO `perm` VALUES ('3', '修改权限');
INSERT INTO `perm` VALUES ('4', '删除权限');
INSERT INTO `perm` VALUES ('5', '管理员权限');
CREATE TABLE `user_perm` (
`id` int(11) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`perm_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user_perm` VALUES ('1', '1', '1,2,3,4');
INSERT INTO `user_perm` VALUES ('2', '2', '1');
INSERT INTO `user_perm` VALUES ('3', '3', '1,2,5');
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--mybatis-plus代码生成-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!--mybatis plus默认的模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--shiro整合springboot-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>
添加配置
spring:
application:
name: shiro_demo
datasource:
url: jdbc:mysql://127.0.0.1:3306/shiro_demo
username:
password:
mybatis-plus:
type-aliases-package: top.shiro_demo.user.entity
mapper-locations: classpath:/top/shiro_demo/user/mapper/*Mapper.xml
global-config:
enable-sql-runner: true
banner: false
db-config:
db-type: mysql
logic-delete-value: 0
logic-not-delete-value: 1
shiro:
loginUrl: /login
shiro-redis:
session-dao:
expire: 3600
redis-manager:
host: 127.0.0.1:6379
timeout: 50000
cache-manager:
expire: 3600
Shiro配置类
@Configuration
public class ShiroConfig {
@Autowired
private RedisSessionDAO redisSessionDAO;
@Autowired
private RedisCacheManager redisCacheManager;
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
//这些路径都放过
chain.addPathDefinition("/401", "anon");
chain.addPathDefinition("/403", "anon");
chain.addPathDefinition("/login", "anon");
chain.addPathDefinition("/error", "anon");
chain.addPathDefinition("/logout", "anon,logout");
//其他路径都检查
chain.addPathDefinition("/**", "authc");
return chain;
}
@Bean
public SessionsSecurityManager securityManager(List<Realm> realms, SessionManager sessionManager){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager(realms);
//Session管理器
manager.setSessionManager(sessionManager);
//配置会话管理
manager.setCacheManager(redisCacheManager);
return manager;
}
@Bean
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//使用redis存储session
sessionManager.setSessionDAO(redisSessionDAO);
// 不在地址栏显示sessionId
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setSessionValidationSchedulerEnabled(true);
return sessionManager;
}
}
自定义Realm
@Component
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private UserPermService userPermService;
@Autowired
private PermService permService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) principalCollection.getPrimaryPrincipal();
Integer userId = user.getId();
UserPerm userPerm = userPermService.selectByUserId(userId);
if (userPerm == null){
return null;
}
//根据用户ID查找权限
Set<String> perms = permService.selectByPermIds(userPerm.getPermId().split(","));
//给用户添加权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(perms);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过手机号查找用户
User user = userService.selectByPhone(token.getUsername());
if (user == null){
throw new UnknownAccountException();
}
if (user.getStatus() == 0){
throw new LockedAccountException();
}
return new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());
}
//加密
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//对密码进行加密处理,使用MD5加密1次
matcher.setHashAlgorithmName("MD5");
matcher.setHashIterations(1);
super.setCredentialsMatcher(matcher);
}
}
登录入口
@RestController
public class LoginController {
//登录
@PostMapping("/login")
public void login(String phone, String password){
SecurityUtils.getSubject().login(new UsernamePasswordToken(phone,password));
}
//登出
@GetMapping("/logout")
public void logout(){
SecurityUtils.getSubject().logout();
}
}
访问该接口的用户,必须具备管理员权限,否则不能访问
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 需要管理员权限才能访问
* @param phone
* @return
*/
@RequiresPermissions("管理员权限")
@GetMapping("/selectByPhone")
public User selectByPhone(String phone){
return userService.selectByPhone(phone);
}
}
实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Perm implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type = IdType.INPUT)
private Integer id;
private String name;
}
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class User implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type = IdType.INPUT)
private Integer id;
private String username;
private String password;
private String phone;
@TableLogic
private Integer status;
private String salt;
}
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserPerm implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type = IdType.INPUT)
private Integer id;
private Integer userId;
private String permId;
}
service
@Service
public class PermServiceImpl implements PermService {
@Autowired
private PermMapper permMapper;
@Override
public Set<String> selectByPermIds(String[] perms) {
return permMapper.selectByPermIds(perms);
}
}
@Service
public class UserPermServiceImpl implements UserPermService {
@Autowired
private UserPermMapper userPermMapper;
@Override
public UserPerm selectByUserId(Integer userId) {
return userPermMapper.selectOne(new QueryWrapper<UserPerm>().eq("user_id",userId));
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User selectByPhone(String phone) {
return userMapper.selectOne(new QueryWrapper<User>().eq("phone",phone));
}
}