SpringSecurity(认证和授权)&权限控制

认证和授权:

        用户登录系统---认证:

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

        用户登录后拥有不同的权限操作---授权:

                用户认证成功后需要为用户授权,就是指定当前用户能够操作那些功能

权限模块数据模型:

        要实现最终的权限控制,需要有一套表结构支撑:

                用户表t_user、权限表t_permission、角色表t_role、菜单表t_menu、用户角色关系表t_user_role、角色权限关系表t_role_permission、角色菜单关系表t_role_menu。

         通过上图可以看到,权限模块共涉及到7张表。在这7张表中,角色表起到了至关重要的作用,其处于核心位置,我们把基于角色的权限控制叫做RBAC,因为用户、权限、菜单都和角色是多对多关系。

分析一下在认证和授权过程中分别会使用到哪些表:

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

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

 

RBAC权限模块数据模型(基于角色的权限控制):

        用户表,角色表,权限表,菜单表

        用户表-----认证

        用户表,角色表-----多对多关系

        权限表,角色表-----多对多关系

        菜单表,角色表-----多对多关系

Spring Security

        官网:Spring Security Spring Security

        是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证和授权的过程。

对应的maven坐标:

pom.xml

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>

SpringSecurity内部封装了Filter(只需要在web.xml容器中配置过滤器---代理过滤器,真实的过滤器在spring容器中配置)

  1. 常见的安全框架

    • Spring的 SpringSecurity

    • Apache的Shiro  http://shiro.apache.org/

配置web.xml:

        1.DelegatingFilterProxy用于整合第三方框架代理过滤器,非真正的过滤器,真正的过滤器需要在spring的配置文件)

        2.springmvc的核心控制器

注: 只需要配置spring-security.xml这个核心配置文件即可在web.xml文件中导出web.xml需要的配置,再在SpringMVC.xml文件中引入spring-security.xml即可

spring-security.xml



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/security
                          http://www.springframework.org/schema/security/spring-security.xsd">

    <!--1匿名访问配置  可匿名访问的资源(不需要登录,权限 角色 就可以访问的资源)-->
    <security:http security="none" pattern="/css/**"/>
    <security:http security="none" pattern="/img/**"/>
    <security:http security="none" pattern="/js/**"/>
    <security:http security="none" pattern="/plugins/**"/>
    <security:http security="none" pattern="/template/**"/>
    <security:http security="none" pattern="/login.html"/>
    <!--2拦截规则配置(pages需要认证访问、登录配置、csrf配置)-->
    <security:http auto-config="true" use-expressions="true">
        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"></security:frame-options>
        </security:headers>
        <!--pages需要认证访问-->
        <security:intercept-url pattern="/pages/**" access="isAuthenticated()"/>
        <!--登录配置-->
        <security:form-login login-page="/login.html" login-processing-url="/login.do"
                             default-target-url="/pages/main.html" always-use-default-target="true"
                             authentication-failure-url="/login.html"/>
        <!--csrf配置-->
        <security:csrf disabled="true"/>

        <!--退出配置-->
        <security:logout logout-url="/logout.do" invalidate-session="true" logout-success-url="/login.html"/>
    </security:http>
    <!--3认证管理器(配置自定义授权类、密码策略)-->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userServiceSecurity">
            <!--密码策略-->
            <security:password-encoder ref="encoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!--注册密码加密策略-->
    <bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <!--开启权限注解-->
    <security:global-method-security pre-post-annotations="enabled"/>
</beans>

密码采用加盐随机的方式

使用注解方式来控制访问权限@PreAuthorize("hasAuthority('相当于权限控制的钥匙')")

常见的密码加密方式有:

        3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码

        MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解

        MD5可进行加盐加密,保证安全

md5在线解密破解,md5解密加密

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

spring security中的BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的。

(1)加密(encode):注册用户时,使用SHA-256+随机盐+密钥把用户输入的密码进行hash处理,得到密码的hash值,然后将其存入数据库中。

(2)密码匹配(matches):用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过Hash处理,是不可逆的),而是使用相同的算法把用户输入的密码进行hash处理,得到密码的hash值,然后将其与从数据库中查询到的密码hash值进行比较。如果两者相同,说明用户输入的密码正确。

这正是为什么处理密码时要用hash算法,而不用加密算法。因为这样处理即使数据库泄漏,黑客也很难破解密码。

测试:

import org.junit.Test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class TestSpringSecurity {
    // SpringSecurity加盐加密
    @Test
    public void testSpringSecurity(){
        // $2a$10$dyIf5fOjCRZs/pYXiBYy8uOiTa1z7I.mpqWlK5B/0icpAKijKCgxe
        // $2a$10$OphM.agzJ55McriN2BzCFeoLZh/z8uL.8dcGx.VCnjLq01vav7qEm

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String s = encoder.encode("abc");
        System.out.println(s);
        String s1 = encoder.encode("abc");
        System.out.println(s1);

        // 进行判断
        boolean b = encoder.matches("abc", "$2a$10$dyIf5fOjCRZs/pYXiBYy8uOiTa1z7I.mpqWlK5B/0icpAKijKCgxe");
        System.out.println(b);
    }
}

加密后字符串的长度为固定的60位。其中:

$是分割符,无意义;

2a是bcrypt加密版本号;

10是cost的值;循环加密的次数,默认10

而后的前22位是salt值;

再然后的字符串就是密码的密文了。

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

<!--2.认证管理器配置-->
<security:authentication-manager>
    <!--user-service-ref="mySecurityService" 认证服务提供者 调用 自定义认证授权类 获取当前用户账号 密码 权限列表-->
    <security:authentication-provider user-service-ref="mySecurityService">
        <!--<security:user-service>-->
        <!--配置账号 密码{noop}admin:明文 权限-->
        <!--<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
            </security:user-service>-->
        <!--密码加密策略-->
        <security:password-encoder ref="encoder"/>
    </security:authentication-provider>
</security:authentication-manager>

<!--注册密码加密策略-->
<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

第二步:修改UserService实现类

package com.security;

import com.itheima.pojo.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自定义认证授权类
 * 实现UserDetailsService接口 框架调用loadUserByUsername得到用户信息(用户名 密码  权限列表)
 */
@Component
public class MySecurityService implements UserDetailsService {

    //模拟数据库中的用户数据
    public static Map<String, User> map = new HashMap<String, User>();
    private  static BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    static {
        com.itheima.pojo.User user1 = new com.itheima.pojo.User();
        user1.setUsername("admin");
        user1.setPassword(encoder.encode("admin"));

        com.itheima.pojo.User user2 = new com.itheima.pojo.User();
        user2.setUsername("zhangsan");
        user2.setPassword(encoder.encode("123"));

        map.put(user1.getUsername(), user1);
        map.put(user2.getUsername(), user2);
    }

    /**
     * 根据用户在登录页面输入的用户名 查询 用户信息(用户名 密码  权限列表)
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.根据用户在登录页面输入的用户名 查询用户对象
        User user = map.get(username);
        //2.如果用户对象不存在,直接return null
        if(user == null){
            return null;
        }
        //3.如果用户对象存在,获取用户密码
        //String password = "{noop}"+user.getPassword();
        String password = user.getPassword();//使用Bcryt密码加密策略校验
        //4.根据当前用户获取用户权限列表,进行授权(现在先写死权限数据,后续从数据库中查询)
        List<GrantedAuthority> list  = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));//添加权限
        //5.将账号 密码  权限列表返回给框架
        //String username:用户名 String password:密码 Collection<? extends GrantedAuthority> authorities:权限列表
        return new org.springframework.security.core.userdetails.User(username,password,list);
    }
}

配置多种校验规则----对页面

        修改spring-security.xml文件:例:相关说明

<!--只要认证通过就可以访问-->
<security:intercept-url pattern="/index.html"  access="isAuthenticated()" />
<security:intercept-url pattern="/a.html"  access="isAuthenticated()" />

<!--拥有add权限就可以访问b.html页面-->
<security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />

<!--拥有ROLE_ADMIN角色就可以访问c.html页面,
    注意:此处虽然写的是ADMIN角色,框架会自动加上前缀ROLE_-->
<security:intercept-url pattern="/c.html"  access="hasRole('ADMIN')" />

<!--拥有ROLE_ADMIN角色就可以访问d.html页面-->
<security:intercept-url pattern="/d.html"  access="hasRole('ABC')" />

测试:

登录后可以访问a.html,b.html,c.html,不能访问d.html(抛出403的异常)

注解方式权限控制(对类)

 退出登录:

        1.定义退出登录链接

        2.在spring-security.xml定义(上面文件中有)

                <!--
                          logout:退出登录
                          logout-url:退出登录操作对应的请求路径
                          logout-success-url:退出登录后的跳转页面
                -->

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值