SpringSecurity原理和实际应用

前提知识

认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

实现认证和授权需要以下七张表

SpringSecurity原理和实际应用_第1张图片

所有用户都存在t_user中

给用户授予不同的权限,权限保存在t_premission中

SpringSecurity原理和实际应用_第2张图片

 角色表t_role是便于为用户授权而建立的表,角色就是一堆权限的集合,角色和权限是多对多的关系

SpringSecurity原理和实际应用_第3张图片

SpringSecurity原理和实际应用_第4张图片

页面的全部菜单保存在t_menu中

SpringSecurity原理和实际应用_第5张图片

 以上四个主表通过三张副表实现两两对应关系,且都是多对多关系

用户user和角色role进行关联

角色role和premission关联

角色role和菜单menu关联

角色表有着重要作用,因为都和角色表有多对多关系

用户表ID和角色表ID相关联,关联信息在user_role表中

用户需要不同角色,赋予不同的角色ID

认证过程:只需要用户表就可以了,在用户登录时可以查询用户表t_user进行校验,判断用户输入的用户名和密码是否正确。

授权过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

Spring Security可以帮助我们来简化认证和授权

项目中应用

1.在web.xml中配置文件


    springSecurityFilterChain
    /*

SpringSecurity原理和实际应用_第6张图片

2.resources提供spring_security配置文件

1.设置拦截规则 



    

2.配置认证管理器


    
        
        
            
            
        
    

{noop}表示明文

SpringSecurity原理和实际应用_第7张图片

 项目启动之后

web.xml


    springSecurityFilterChain
    /*

spring_security配置文件


        

配置可以匿名访问的资源 

 
    
    
    
    

使用自己指定的页面作为登录页面

username,password对应登录界面的参数

login-processing-url:登录表单提交的地址,后续不需要创建controller类,框架会自动处理 default-target-url:认证成功跳转界面

authentication-failure-url:失败跳转界面


        

配置crsf项


        

实现从数据库中查询用户

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个实现UserDetailsService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。

1.创建一个service类

/**
 * 动态给用户授予角色和权限
 */
@Component
public class SpringSecurityUserService implements UserDetailsService {

    @Reference //注意:此处要通过dubbo远程调用用户服务
    private UserService userService;

    //根据用户名查询用户信息
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            //用户名不存在
            return null;
        }
        //动态为当前用户授权
        List list = new ArrayList<>();
        Set roles = user.getRoles();
        for (Role role : roles) {
            //遍历角色,为用户授予角色
            list.add(new SimpleGrantedAuthority(role.getKeyword()));
            Set permissions = role.getPermissions();
            for (Permission permission : permissions) {
                //遍历权限集合,为用户授权
                list.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }
        org.springframework.security.core.userdetails.User securityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(),list);
        return securityUser;
    }
}
//User是框架提供的User,根据用户名查询数据库信息(包含存储的密码,将用户信息返回给框架,框架会进行密码比对
User user = userService.findByUsername(username);

密码加密

bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题

第一步:在spring-security.xml文件中指定密码加密对象

SpringSecurity原理和实际应用_第8张图片

user1.setPassword(passwordEncoder.encode("admin"));

数据库中的密码是经过加密之后的密码,前端提交的密码会自动加密并和数据库中作比较

记得在xml文件中开启注解控制

SpringSecurity原理和实际应用_第9张图片

配置多种校验规则












注解方式权限控制

针对不同页面访问的权限要求,SpringSecurity提供了注解方式权限控制,可以精确到方法控制级别

第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller


第二步:在spring-security.xml文件中开启权限注解支持


controller类中的代码

@RestController
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
    public String add(){
        System.out.println("add...");
        return "success";
    }

    @RequestMapping("/delete")
    @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色才能调用当前方法
    public String delete(){
        System.out.println("delete...");
        return "success";
    }
}

SpringSecurity原理和实际应用_第10张图片

注意注解代码中的权限必须和数据中权限表的关键字一致

七牛云绑定自定义域名

1.新建存储空间,并使用javaSDK方式操作空间

Java SDK的所有的功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access Key和Secret Key。

后端代码

新建上传文件工具类Utils。工具类中需要配置云存储的相关参数,

public class QiniuUtils {
    public  static String accessKey = "YLRQlxIU5IyLEGSbhFt-hAYKvGY0_zNk_eABhM8t";
    public  static String secretKey = "eswtzJogA9qDSne9Z9uZM8Kt8QNS0AYPNS971dxX";
    public  static String bucket = "ahtcm-space-01";

    //上传文件(方法1
    public static void upload2Qiniu(String filePath,String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Region.region2());//使用华南机房
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(filePath, fileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println("文件名:" + putRet.key);
            System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }
    }

    //上传文件(法2
    public static void upload2Qiniu(byte[] bytes, String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Region.region2());//使用华南机房
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);

        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = fileName;
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(bytes, key, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println("文件名:" + putRet.key);
            System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }
    }

    //删除文件
    public static void deleteFileFromQiniu(String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone0());
        String key = fileName;
        Auth auth = Auth.create(accessKey, secretKey);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
            bucketManager.delete(bucket, key);
        } catch (QiniuException ex) {
            //如果遇到异常,说明删除失败
            System.err.println(ex.code());
            System.err.println(ex.response.toString());
        }
    }
}

在controller中调用工具类方法进行文件上传

//文件上传,图片上传
    @RequestMapping("/upload")
    public Result upload(@RequestParam("imgFile") MultipartFile imgFile) {//使用springmvc的上传组件来上传文件,后台接收到文件
        //System.out.println(imgFile);
        //获取原始文件名
        String originalFilename = imgFile.getOriginalFilename();
        int lastIndexOf = originalFilename.lastIndexOf(".");
        //获取文件后缀
        String suffix = originalFilename.substring(lastIndexOf - 1);
        //使用UUID随机产生文件名称,防止同名文件覆盖
        String fileName = UUID.randomUUID().toString() + suffix;
        try {
            QiniuUtils.upload2Qiniu(imgFile.getBytes(), fileName);//使用七牛云工具类的bytes数组上传文件方法,第二个参数为文件名
            //将上传图片名称存入Redis,基于Redis的Set集合存储
            String urlFileName = "http://ruh9fwjil.hn-bkt.clouddn.com/" + fileName;
            jedisPool.getResource().sadd(RedisConstant.RECORD_PIC_RESOURCES,urlFileName);
            //图片上传成功
            Result result = new Result(true, MessageConstant.PIC_UPLOAD_SUCCESS);
            result.setData(fileName);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            //图片上传失败
            return new Result(false, MessageConstant.PIC_UPLOAD_FAIL);
        }
    }

如果网络地址配置正确,但前端不能实时查看到文件,去云平台查看是否是将空间设置为了私有空间,私有空间中的bucket不能被直接访问。

你可能感兴趣的:(安全)