当前位置:   article > 正文

SpringSecurity原理和实际应用_security+实际操作

security+实际操作

前提知识

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

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

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

所有用户都存在t_user中

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

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

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

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

用户user和角色role进行关联

角色role和premission关联

角色role和菜单menu关联

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

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

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

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

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

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

项目中应用

1.在web.xml中配置文件

  1. <!--/*表示安全框架启动之后会扫描所有的请求-->
  2. <filter-name>springSecurityFilterChain</filter-name>
  3. <url-pattern>/*</url-pattern>

2.resources提供spring_security配置文件

1.设置拦截规则 

  1. <security:http auto-config="true" use-expressions="true">
  2. <security:intercept-url pattern="/pages/**" access="isAuthenticated()" />

2.配置认证管理器

  1. <!--认证管理器,用于处理认证操作-->
  2. <security:authentication-manager>
  3. <!--认证提供者,执行具体的认证逻辑,此处爆红不影响使用-->
  4. <security:authentication-provider user-service-ref="springSecurityUserService">
  5. <!--指定密码加密策略-->
  6. <security:password-encoder ref="passwordEncoder" />
  7. </security:authentication-provider>
  8. </security:authentication-manager>

{noop}表示明文

 项目启动之后

web.xml

  1. <!--/*表示安全框架启动之后会扫描所有的请求-->
  2. <filter-name>springSecurityFilterChain</filter-name>
  3. <url-pattern>/*</url-pattern>

spring_security配置文件

  1. <!--
  2. intercept-url:定义一个拦截规则
  3. pattern:对哪些url进行权限控制
  4. access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
  5. isAuthenticated():已经经过认证(不是匿名用户),只要是
  6. -->
  7. <security:intercept-url pattern="/pages/**" access="isAuthenticated()" />

配置可以匿名访问的资源 

  1. <!--
  2. http:用于定义相关权限控制
  3. 指定哪些资源不需要进行权限校验,匿名(不登陆)即可访问以下资源,可以使用通配符
  4. -->
  5. <security:http security="none" pattern="/js/**" />
  6. <security:http security="none" pattern="/css/**" />
  7. <security:http security="none" pattern="/img/**" />
  8. <security:http security="none" pattern="/plugins/**" />

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

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

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

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

  1. <!--form-login:定义表单登录信息-->
  2. <security:form-login login-page="/login.html"
  3. username-parameter="username"
  4. password-parameter="password"
  5. login-processing-url="/login.do"
  6. default-target-url="/pages/main.html"
  7. always-use-default-target="true"
  8. authentication-failure-url="/login.html"
  9. />

配置crsf项

  1. <!--
  2. csrf:对应CsrfFilter过滤器
  3. disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403)。默认是开启的,true表示关闭
  4. -->
  5. <security:csrf disabled="true"></security:csrf>

实现从数据库中查询用户

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

1.创建一个service类

  1. /**
  2. * 动态给用户授予角色和权限
  3. */
  4. @Component
  5. public class SpringSecurityUserService implements UserDetailsService {
  6. @Reference //注意:此处要通过dubbo远程调用用户服务
  7. private UserService userService;
  8. //根据用户名查询用户信息
  9. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  10. User user = userService.findByUsername(username);
  11. if (user == null) {
  12. //用户名不存在
  13. return null;
  14. }
  15. //动态为当前用户授权
  16. List<GrantedAuthority> list = new ArrayList<>();
  17. Set<Role> roles = user.getRoles();
  18. for (Role role : roles) {
  19. //遍历角色,为用户授予角色
  20. list.add(new SimpleGrantedAuthority(role.getKeyword()));
  21. Set<Permission> permissions = role.getPermissions();
  22. for (Permission permission : permissions) {
  23. //遍历权限集合,为用户授权
  24. list.add(new SimpleGrantedAuthority(permission.getKeyword()));
  25. }
  26. }
  27. org.springframework.security.core.userdetails.User securityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(),list);
  28. return securityUser;
  29. }
  30. }
//User是框架提供的User,根据用户名查询数据库信息(包含存储的密码,将用户信息返回给框架,框架会进行密码比对
User user = userService.findByUsername(username);

密码加密

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

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

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

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

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

配置多种校验规则

  1. <!--只要认证通过就可以访问-->
  2. <security:intercept-url pattern="/index.jsp" access="isAuthenticated()" />
  3. <security:intercept-url pattern="/a.html" access="isAuthenticated()" />
  4. <!--拥有add权限就可以访问b.html页面-->
  5. <security:intercept-url pattern="/b.html" access="hasAuthority('add')" />
  6. <!--拥有ROLE_ADMIN角色就可以访问c.html页面-->
  7. <security:intercept-url pattern="/c.html" access="hasRole('ROLE_ADMIN')" />
  8. <!--拥有ROLE_ADMIN角色就可以访问d.html页面,
  9. 注意:此处虽然写的是ADMIN角色,框架会自动加上前缀ROLE_-->
  10. <security:intercept-url pattern="/d.html" access="hasRole('ADMIN')" />

注解方式权限控制

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

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

  1. <mvc:annotation-driven></mvc:annotation-driven>
  2. <context:component-scan base-package="com.itheima.controller"></context:component-scan>

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

  1. <!--开启注解方式权限控制-->
  2. <security:global-method-security pre-post-annotations="enabled" />

controller类中的代码

  1. @RestController
  2. @RequestMapping("/hello")
  3. public class HelloController {
  4. @RequestMapping("/add")
  5. @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
  6. public String add(){
  7. System.out.println("add...");
  8. return "success";
  9. }
  10. @RequestMapping("/delete")
  11. @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色才能调用当前方法
  12. public String delete(){
  13. System.out.println("delete...");
  14. return "success";
  15. }
  16. }

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

七牛云绑定自定义域名

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

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

后端代码

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

  1. public class QiniuUtils {
  2. public static String accessKey = "YLRQlxIU5IyLEGSbhFt-hAYKvGY0_zNk_eABhM8t";
  3. public static String secretKey = "eswtzJogA9qDSne9Z9uZM8Kt8QNS0AYPNS971dxX";
  4. public static String bucket = "ahtcm-space-01";
  5. //上传文件(方法1
  6. public static void upload2Qiniu(String filePath,String fileName){
  7. //构造一个带指定Zone对象的配置类
  8. Configuration cfg = new Configuration(Region.region2());//使用华南机房
  9. cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
  10. //...其他参数参考类注释
  11. UploadManager uploadManager = new UploadManager(cfg);
  12. //...生成上传凭证,然后准备上传
  13. Auth auth = Auth.create(accessKey, secretKey);
  14. String upToken = auth.uploadToken(bucket);
  15. try {
  16. Response response = uploadManager.put(filePath, fileName, upToken);
  17. //解析上传成功的结果
  18. DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
  19. System.out.println("文件名:" + putRet.key);
  20. System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
  21. } catch (QiniuException ex) {
  22. Response r = ex.response;
  23. System.err.println(r.toString());
  24. try {
  25. System.err.println(r.bodyString());
  26. } catch (QiniuException ex2) {
  27. //ignore
  28. }
  29. }
  30. }
  31. //上传文件(法2
  32. public static void upload2Qiniu(byte[] bytes, String fileName){
  33. //构造一个带指定Zone对象的配置类
  34. Configuration cfg = new Configuration(Region.region2());//使用华南机房
  35. //...其他参数参考类注释
  36. UploadManager uploadManager = new UploadManager(cfg);
  37. //默认不指定key的情况下,以文件内容的hash值作为文件名
  38. String key = fileName;
  39. Auth auth = Auth.create(accessKey, secretKey);
  40. String upToken = auth.uploadToken(bucket);
  41. try {
  42. Response response = uploadManager.put(bytes, key, upToken);
  43. //解析上传成功的结果
  44. DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
  45. System.out.println("文件名:" + putRet.key);
  46. System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
  47. } catch (QiniuException ex) {
  48. Response r = ex.response;
  49. System.err.println(r.toString());
  50. try {
  51. System.err.println(r.bodyString());
  52. } catch (QiniuException ex2) {
  53. //ignore
  54. }
  55. }
  56. }
  57. //删除文件
  58. public static void deleteFileFromQiniu(String fileName){
  59. //构造一个带指定Zone对象的配置类
  60. Configuration cfg = new Configuration(Zone.zone0());
  61. String key = fileName;
  62. Auth auth = Auth.create(accessKey, secretKey);
  63. BucketManager bucketManager = new BucketManager(auth, cfg);
  64. try {
  65. bucketManager.delete(bucket, key);
  66. } catch (QiniuException ex) {
  67. //如果遇到异常,说明删除失败
  68. System.err.println(ex.code());
  69. System.err.println(ex.response.toString());
  70. }
  71. }
  72. }

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/67701
推荐阅读
相关标签
  

闽ICP备14008679号

        
cppcmd=keepalive&