美文网首页springsecurity
Spring Security-原始篇(XML配置版)

Spring Security-原始篇(XML配置版)

作者: 石头耳东 | 来源:发表于2022-05-14 23:20 被阅读0次

该篇章会跳过Spring、SpringAOP、MyBatis基础部分的讨论,专注于Spring Security部分的讲解。

零、本文纲要

  • 一、认证授权
  1. 认证授权
  2. 对象
  3. Spring Security
  • 二、Spring Security过滤器链

补充:CsrfFilter(跨域伪造请求过滤)

  • 三、快速入门
  1. 引入配置文件
  2. spring-security配置文件-配置拦截路径、登录、登出、自动配置加载
  3. spring-security配置文件-配置静态资源放行
  4. spring-security配置文件-设置认证用户的来源
  5. 实现UserDetailsService接口

补充:初始数据获取

一、认证授权

1. 认证授权

① 认证:通过用户名和密码成功登陆系统后,让系统得到当前用户的角色身份;

② 授权:系统根据当前用户的角色,给其授予对应可以操作的权限资源。

2. 对象

① 用户:主要包含用户名,密码和当前用户的角色信息,可实现认证操作;

② 角色:主要包含角色名称,角色描述和当前角色拥有的权限信息,可实现授权操作;

③ 权限:权限也可以称为菜单,主要包含当前权限名称,url地址等信息,可实现动态展示菜单。

一般根据对象我们也会设置相应的表格,如下:

/*sys_permission权限表*/
CREATE TABLE `sys_permission` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `permission_NAME` varchar(30) DEFAULT NULL COMMENT '菜单名称',
  `permission_url` varchar(100) DEFAULT NULL COMMENT '菜单地址',
  `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '父菜单id',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*sys_role角色表*/
CREATE TABLE `sys_role` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `ROLE_NAME` varchar(30) DEFAULT NULL COMMENT '角色名称',
  `ROLE_DESC` varchar(60) DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*sys_role_permission角色-权限关系表*/
CREATE TABLE `sys_role_permission` (
  `RID` int(11) NOT NULL COMMENT '角色编号',
  `PID` int(11) NOT NULL COMMENT '权限编号',
  PRIMARY KEY (`RID`,`PID`),
  KEY `FK_Reference_12` (`PID`),
  CONSTRAINT `FK_Reference_11` FOREIGN KEY (`RID`) REFERENCES `sys_role` (`ID`),
  CONSTRAINT `FK_Reference_12` FOREIGN KEY (`PID`) REFERENCES `sys_permission` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*sys_user用户表*/
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `password` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
  `status` int(1) DEFAULT '1' COMMENT '1开启0关闭',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*sys_user_role用户-角色表*/
CREATE TABLE `sys_user_role` (
  `UID` int(11) NOT NULL COMMENT '用户编号',
  `RID` int(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `sys_role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `sys_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3. Spring Security

Spring Security是spring采用AOP思想,基于servlet过滤器实现的安全框架。

基础依赖:

① spring-security-core:核心包,任何Spring Security功能都需要此包;

② spring-security-web:web工程必备,包含过滤器和相关的Web安全基础结构代码;

③ spring-security-config:用于解析xml配置文件,用到Spring Security的xml配置文件的就要用到此包;

④ spring-security-taglibs:Spring Security提供的动态标签库,jsp页面可以用。

注意:
spring-security-web包中包含有spring-security-core;
spring-security-taglibs包中包含有spring-security-web。

二、Spring Security过滤器链

Spring Security默认提供的过滤器链,如下:

  1. SecurityContextPersistenceFilter(容器)
  2. WebAsyncManagerIntegrationFilter(集成web开发)
  3. HeaderWriterFilter
  4. CsrfFilter(跨域伪造请求过滤)
  5. LogoutFilter(登出过滤)
  6. UsernamePasswordAuthenticationFilter(用户认证授权)
  7. DefaultLoginPageGeneratingFilter(默认登录页面)
  8. DefaultLogoutPageGeneratingFilter(默认退出页面)
  9. BasicAuthenticationFilter(以Basic开头的头信息)
  10. RequestCacheAwareFilter(缓存请求)
  11. SecurityContextHolderAwareRequestFilter(升级的容器)
  12. AnonymousAuthenticationFilter(匿名认证过滤器)
  13. SessionManagementFilter(会话管理)
  14. ExceptionTranslationFilter(异常处理)
  15. FilterSecurityInterceptor(获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权
    限)

补充:CsrfFilter(跨域伪造请求过滤)

  • ① CsrfFilter#doFilterInternal方法
@Override
protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
    request.setAttribute(HttpServletResponse.class.getName(), response);
    ...
    //此处关注CsrfFilter类的requireCsrfProtectionMatcher属性
    if (!this.requireCsrfProtectionMatcher.matches(request)) {
        filterChain.doFilter(request, response);
        return;
    }
    ...
    filterChain.doFilter(request, response);
}
  • ② DefaultRequiresCsrfMatcher类

可以看到:

"GET", "HEAD", "TRACE", "OPTIONS"四类请求可以直接通过;

除去上面四类,包括"POST","DELETE","PUT"都要被验证携带_csrf对应的token才能通过;

private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
    private final HashSet<String> allowedMethods = new HashSet<>(
            Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
    @Override
    public boolean matches(HttpServletRequest request) {
        return !this.allowedMethods.contains(request.getMethod());
    }
}
  • ③ 处理方式
<%--引入security标签库--%>
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>

<%--在form表单内部添加如下标签,Spring Security框架会自动完成token的注入与校验--%>
<security:csrfInput/>

三、快速入门

0. 引入配置文件

applicationContext.xml配置文件中配置资源引入,如下:

<!--引入SpringSecurity配置文件-->
<import resource="classpath:spring-security.xml"/>

相当于:

@Import(SecurityConfig.class)
public class SpringConfig{...}

1. spring-security配置文件-配置拦截路径、登录、登出、自动配置加载

  • ① 配置加载配置文件

<security:http auto-config="true" use-expressions="true">

auto-config="true" 表示自动加载SpringSecurity的配置文件;

use-expressions="true" 表示使用Spring的EL表达式来配置;

  • ② 配置拦截资源

<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>

pattern="/**" 表示拦截所有资源;

access="hasAnyRole('ROLE_USER') 表示只有ROLE_USER角色才能访问资源;

  • ③ 配置认证信息

<security:form-login login-page="/login.jsp" login-processing-url="/login" default-target-url="/index.jsp" authentication-failure-url="/failer.jsp"/>

login-page="/login.jsp" 表示访问的登录页面

login-processing-url="/login" 表示访问的请求路径

default-target-url="/index.jsp" 表示默认认证成功的跳转页面

authentication-failure-url="/failer.jsp" 表示认证失败的跳转页面

  • ④ 配置页面匿名访问

<security:intercept-url pattern="/login.jsp" access="permitAll()"/>

pattern="/login.jsp" 表示访问的资源

access="permitAll()" 表示允许该路径通过过滤器,但不会绕开Spring Security的过滤器验证

  • ⑤ 配置退出登录信息

<security:logout logout-url="/logout" logout-success-url="/login.jsp"/>

logout-url="/logout" 表示登出的访问路径

logout-success-url="/login.jsp" 表示登出成功的跳转界面

<!--配置SpringSecurity-->
<!--
    auto-config="true" 表示自动加载SpringSecurity的配置文件
    use-expressions="true" 表示使用Spring的EL表达式来配置
-->
<security:http auto-config="true" use-expressions="true">
    <!--让认证页面可以匿名访问-->
    <security:intercept-url pattern="/login.jsp" access="permitAll()"/>
    <!--拦截资源-->
    <!--
        pattern="/**" 表示拦截所有资源
        access="hasAnyRole('ROLE_USER') 表示只有ROLE_USER角色才能访问资源
    -->
    <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
    <!--配置认证信息-->
    <security:form-login login-page="/login.jsp"
                         login-processing-url="/login"
                         default-target-url="/index.jsp"
                         authentication-failure-url="/failer.jsp"/>
    <!--配置退出登录信息-->
    <security:logout logout-url="/logout"
                     logout-success-url="/login.jsp"/>
    <!--禁用CSRF拦截的过滤器-->
    <!--<security:csrf disabled="true"/>-->
</security:http>

2. spring-security配置文件-配置静态资源放行

特殊的:/failer.jsp需要配置放行,不然登录失败页面无法正常显示。

<!--释放静态资源-->
<security:http pattern="css/**" security="none"/>
<security:http pattern="img/**" security="none"/>
<security:http pattern="plugins/**" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>

3. spring-security配置文件-设置认证用户的来源

user-service-ref="userServiceImpl" 表示用户认证服务由该实现类管理;

ref="passwordEncoder" 表示使用此种加密工具类;

<!--把加密对象放入IOC容器中-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

<!--设置SpringSecurity认证用户的来源-->
<!--
    SpringSecurity默认是时加密认证的
    加上{noop}表示不加密认证
-->
<security:authentication-manager>
    <security:authentication-provider user-service-ref="userServiceImpl">
        <security:password-encoder ref="passwordEncoder"/>
        <!--<security:user-service>
            <security:user name="user" password="{noop}user"
                           authorities="ROLE_USER" />
            <security:user name="admin" password="{noop}admin"
                           authorities="ROLE_ADMIN" />
        </security:user-service>-->
    </security:authentication-provider>
</security:authentication-manager>

4. 实现UserDetailsService接口

user.setPassword(passwordEncoder.encode(user.getPassword()));:保存用户信息时需要进行密码加密;

实现UserDetailsService接口,重写loadUserByUsername方法,认证通过的返回UserDetails类;

public interface UserService extends UserDetailsService{...}

@Service
@Transactional
public class UserServiceImpl implements UserService {
    ...
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    
    @Override
    public void save(SysUser user) {
        //使用BCryptPasswordEncoder进行密码加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userDao.save(user);
    }
    
    /**
     * 认证业务
     * @param username 用户在浏览器输入的用户名
     * @return UserDetails 是Spring Security自己的用户对象
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            //根据用户名做查询
            SysUser sysUser = userDao.findByName(username);
            if (sysUser == null) {
                return null;
            }

            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            List<SysRole> roles = sysUser.getRoles();
            for (SysRole role : roles) {
                //动态赋予用户角色信息
                authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
            }
            //注意:{noop}后面的密码Spring Security会认定为原文
            UserDetails userDetails = new User(
                    sysUser.getUsername(),
                    sysUser.getPassword(), //"{noop}" + sysUser.getPassword(),
                    authorities);
            return userDetails;
        } catch (Exception e) {
            e.printStackTrace();
            //Spring Security认为null即为认证失败
            return null;
        }
    }
    ...
}

补充:初始数据获取

在引入Spring Security配置文件之前,需要先获取一下加密的密码,这样我们初始账户在开起Spring Security认证授权后才可以正常登录。获取加密密码串的简单测试方法如下:

@Test
public void encodeTest(){
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    String encode = encoder.encode("123456");
    //$2a$10$Vr5KdR6IR7SmGHSRGmV4puvm3RZFaWFwV5M1/WZ5pXKULatetf2.i
    //$2a$10$qz98Xl2hA.FMyH13eDmf3.Pyc9.n1xsImnucyVBSCp39p3a9iH3Ye
    System.out.println(encode);
}

四、结尾

以上即为Spring Security-原始篇(XML配置版)的基础内容,感谢阅读。

相关文章

网友评论

    本文标题:Spring Security-原始篇(XML配置版)

    本文链接:https://www.haomeiwen.com/subject/zecvurtx.html