1.权限管理
1.1 权限管理概述
RBAC(Role Based Access Control) :某个用户拥有什么角色,被允许做什么事情(权限)
用户登录—>分配角色---->(权限关联映射)---->鉴权(拥有什么什么权限)
1.2 shiro架构
Subject(用户):当前的操作用户 获取当前用户Subject currentUser = SecurityUtils.getSubject()
SecurityManager(安全管理器):Shiro的核心,负责与其他组件进行交互,实现 subject 委托的各种功能
Realms(数据源) :Realm会查找相关数据源,充当与安全管理间的桥梁,经过Realm找到数据源进行认证,授权等操作
Authenticator(认证器): 用于认证,从 Realm 数据源取得数据之后进行执行认证流程处理。
Authorizer(授权器):用户访问控制授权,决定用户是否拥有执行指定操作的权限。
SessionManager (会话管理器):支持会话管理
CacheManager (缓存管理器):用于缓存认证授权信息
Cryptography(加密组件):提供了加密解密的工具包
2.案例实现
2.1数据库
2.2 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- SECURITY begin -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
2.3 yaml配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/my_bank?serverTimezone=GMT
password: ******
username: root
mybatis-plus:
configuration:
database: MySQL
show-sql: true
server:
port: 8081
2.4 属性类
@Data
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@TableName("pe_user")
public class User implements Serializable {
private String id;
private String username;
private String password;
private String salt;
private Set<Role> roles=new HashSet<Role>();
}
@Getter
@Setter
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Role implements Serializable {
private static final long serialVersionUID = 594829320797158219L;
private String id;
private String name;
private String description;
private Set<User> users = new HashSet<User>(0);
private Set<Permission> permissions = new HashSet<Permission>(0);
}
@Setter
@Getter
@NoArgsConstructor
public class Permission implements Serializable {
private static final long serialVersionUID = -4990810027542971546L;
private String id;
private String name;
private String code;
private String description;
}
2.5 config配置类
@Configuration
public class ShiroConfiguration {
/**
* 1.创建shiro自带cookie对象
*/
@Bean
public SimpleCookie sessionIdCookie() {
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setName("ShiroSession");
return simpleCookie;
}
//2.创建realm
@Bean
public MyRealm getRealm() {
return new MyRealm();
}
/**
* 3.创建会话管理器
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(false);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setGlobalSessionTimeout(3600000);
return sessionManager;
}
//4.创建安全管理器
@Bean
public SecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getRealm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 5.保证实现了Shiro内部lifecycle函数的bean执行
*/
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 6.开启对shiro注解的支持
* AOP式方法级权限检查
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/**
* 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
return authorizationAttributeSourceAdvisor;
}
// 8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
@Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(defaultWebSecurityManager());
filterFactoryBean.setLoginUrl("/autherror");
Map<String, String> filterMap = new LinkedHashMap<>();
//key:请求规则 value:过滤器名称
filterMap.put("/login", "anon");//当前请求地址可以匿名访问
filterMap.put("/user/**", "authc");//当前请求地址必须认证之后可以访问
//在过滤器工程内设置系统过滤器
filterFactoryBean.setFilterChainDefinitionMap(filterMap);
return filterFactoryBean;
}
}
2.6 数据访问层
@Mapper
public interface UserDao{
@Select("select * from pe_user where username=#{username}")
User findByUsername(String username);
@Results({
@Result(column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "salt", property = "salt"),
@Result(column = "id",property = "roles",
many = @Many(select = "com.cyh.dao.RoleDao.findRoleById"))
})
@Select("select * from pe_user where id=#{id}")
public User findAllById(String id);
}
@Mapper
public interface RoleDao {
@Results({
@Result(column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "description", property = "description"),
@Result(column = "id",property = "permissions",
many = @Many(select = "com.cyh.dao.PermissionDao.findPerById"))
})
@Select("select * from pe_role as pr join pe_user_role AS pur on pur.role_id = pr.id where pur.user_id = #{id}")
Role findRoleById(String id);
}
@Mapper
public interface PermissionDao {
@Results({
@Result(column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "code",property = "code"),
@Result(column = "description", property = "description")
})
@Select("select * from pe_permission as pp join pe_role_permission as prp on prp.permission_id = pp.id where prp.role_id = #{id}")
Permission findPerById(String id);
}
2.7 业务层
public interface IUserService {
User findByUsername(String name);
User finAllById(String id);
}
@Service
public class UserService implements IUserService{
@Autowired(required = false)
private UserDao userDao;
@Override
public User findByUsername(String name) {
User user1 = userDao.findByUsername(name);
return user1;
}
@Override
public User finAllById(String id) {
return userDao.findAllById(id);
}
}
2.8控制层
@RestController
public class UserController {
@Autowired
private IUserService service;
@RequestMapping(value = "/user/find",method = RequestMethod.POST)
public User findByname(String name){
User user1=service.findByUsername(name);
return user1;
}
@RequestMapping(value = "/findAll")
public User findAllById(String id){
User user = service.finAllById(id);
return user;
}
@RequiresPermissions("user-home")
@RequestMapping(value = "/user/home")
public String home() {
return "访问个人主页成功";
}
@RequiresPermissions("user-add")
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String add() {
return "添加用户成功";
}
@RequiresPermissions("user-find")
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String find() {
return "查询用户成功";
}
@RequiresPermissions("user-update")
@RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)
public String update(@PathVariable String id) {
return "更新用户成功";
}
@RequiresPermissions("user-delete")
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
public String delete(@PathVariable String id) {
return "删除用户成功";
}
@RequestMapping(value="/login")
public String login(User user) {
try {
//1.构造登录令牌
UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());
//2.获取subject
Subject subject = SecurityUtils.getSubject();
//3.调用subject进行登录
subject.login(upToken);
return "登录成功";
}catch (Exception e) {
return "用户名或密码错误";
}
}
@RequestMapping(value="/autherror")
public String autherror() {
return "未登录";
}
}
2.9 注册realm
public class MyRealm extends AuthorizingRealm {
@Autowired
private IUserService service;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String id = (String) principalCollection.getPrimaryPrincipal();
//System.out.println(id);
User user = service.finAllById(id);
Set<String> roles = new HashSet<>();
Set<String> perms = new HashSet<>();
for (Role role : user.getRoles()) {
roles.add(role.getName());
for (Permission perm : role.getPermissions()) {
perms.add(perm.getCode());
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(perms);
info.setRoles(roles);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.获取登录的用户名密码(token)
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();//用户录入的账号
//2.根据用户名查询数据库
//mybatis情景下:user对象中包含ID,name,pwd(匿名)
//JPA情景下:user对象中包含ID,name,pwd(匿名),set<角色>,set<权限>
User user = service.findByUsername(username);
//3.判断用户是否存在或者密码是否一致
if (user != null) {
//4.如果一致返回安全数据
//构造方法:安全数据,密码(匿名),混淆字符串(salt),realm域名
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getId(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), "myRealm");
return info;
}
//5.不一致,返回null(抛出异常)
return null;
}
@PostConstruct
public void initCredentialsMatcher() {
//指定密码算法
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
//指定迭代次数
hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);
//生效密码比较器
setCredentialsMatcher(hashedCredentialsMatcher);
}
}
2.10 异常处理
@ControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(value = AuthorizationException.class)
@ResponseBody
public String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e) {
return "未授权-异常处理器实现";
}
}
2.11 加密处理
public class DigestsUtil {
public static final String SHA1 = "SHA-1";
public static final Integer COUNTS =369;
/**
* @Description sha1方法
* @param input 需要散列字符串
* @param salt 盐字符串
* @return
*/
public static String show(String input, String salt) {
return new SimpleHash(SHA1, input, salt,COUNTS).toString();
}
/**
* @Description 随机获得salt字符串
* @return
*/
public static String generateSalt(){
SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
return randomNumberGenerator.nextBytes().toHex();
}
/**
* @Description 生成密码字符密文和salt密文
* @param
* @return
*/
public static Map<String,String> entryptPassword(String passwordPlain) {
Map<String,String> map = new HashMap<>();
String salt = generateSalt();
String password =show(passwordPlain,salt);
map.put("salt", salt);
map.put("password", password);
return map;
}
public static void main(String[] args) {
String name = "刘彼得";
String pwd = "654321";
Map map = entryptPassword(pwd);
System.out.println(map.toString());
}
}