Spring Security基础入门

news2024/11/26 22:46:29

基础概念

什么是认证

认证:用户认证就是判断一个用户的身份身份合法的过程,用户去访问系统资源的时候系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户密码登录,二维码登录,手机短信登录,指纹认证等方式。

其中Spring Security使用的是OAuth2.0认证授权协议。

什么是会话

用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保存到会话中,会话就是系统为了保持用户当前用户的登录状态所提供的机制,常见的有基于Session的方式、基于token等方式

基于Session的认证方式

他的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话中),发给客户端的session_id存放到cookie中,这样用户客户端请求时带上session_id就可以验证服务器是否存在session数据,以此完成用户的合法校验。当用户退出系统或session过期销毁时,客户端的session_id就无效。

方法含义
HttpSession getSession(Boolean create)获取当前HttpSession对象
void setAttribute(String name, Object value)向session中存放对象
void removeAttribute(String name)移除session中的对象
Object getAttribute(String name)从session中获取对象

基于Token方式

他的交互流程是,用户认证成功后,服务端生成一个token发送给客户端,客户端可以放到Cookie中或localStorage等存储中,每次请求时带上token,服务端收到的token通过验证后即可确认用户身份。

对比总结

基于session的认证方式有servlet规范定制的,服务端要存储session信息都要占用内存资源,客户端需要支持Cookie;基于token的方式则一般不需要服务器存储token,并且不限制客户端的存储方式。如今移动互联网时代更多类型的用户端需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于token的方式更合理。

什么是授权

授权:授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

授权数据模型

授权可简单理解为who对what(which)进行how操作。包括如下:

1、who,即主体,主体一般指用户,也可以是程序,需要访问系统中的资源。

2、what,即资源,如系统菜单,页面,按钮,代码方法,系统商品信息,系统订单等。系统菜单,页面,按钮,代码方法属于系统功能资源,对于web系统每个资源通常对应一个URL,系统商品信息、订单信息等属于实体资源(数据资源),实体资源由资源类型和资源示例组成,比如商品信息为资源类型,商品编号如001为资源的实例

3、how,权限/许可(Permission),规定了用户对资源的操作许可,权限离开资源没有意义,如用户查询权限,用户添加权限,某个代码方法的调用权限,编号为001的用户修改权限等,通过权限可知用户对哪些资源都有哪些许可操作。

主体、资源、权限关系如下图

在这里插入图片描述

企业开发中权限表设计如下

资源(资源ID、资源名称、访问地址)

权限(资源ID、权限标识、权限名称、资源ID)

合并为

权限(权限ID、权限标识、权限名称、资源名称、资源访问地址)

数据模型之间的关系如下:

在这里插入图片描述

RBAC

基于角色访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

yes继续访问
no
查询工资信息
判断主体是否具有总经理角色
无法访问处理

基于资源访问控制

RBAC基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权,比如:用户必须具有查询工资权限才能查询员工工资信息等,访问控制流程如下:

YES继续访问
NO
查询工资信息
判断主体身份具有查询工资的权限
无权访问

Spring Security

简介

Spring Security是基于Spring框架,提供一套web应用安全性解决方案。一般来说,web应用的安全性主要包括用户认证(Authentication)和用户授权(Authorization)两部分。这两点是Spring Security的核心功能。

用户认证:是验证某个用户是否为系统中的合法主体,也就是用户能否访问该系统。

用户授权:是验证某个用户是否有权限执行某个操作。在一个系统中,不同的用户所具有的权限不同的。

spring security在项目中进行应用的时候需要登录才能访问接口,否则401.默认用户名:user,密码:每次启动项目后自动生成在日志中输出

引入Maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

快速开始

在controller中编写对应的java代码

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/add")
    public String add() {
        return "hello world";
    }
}

浏览器访问该接口需要输入用户名以及密码,默认用户名是user,密码有控制台日志输出

在这里插入图片描述

postman调用接口,需要在Athorization中输入用户名以及密码,注意Type选择的是Basic Auth

自定义用户名密码

可以在application.yml文件中添加以下配置即可实现

spring:
  security:
    user:
      name: user
      password: pwd123

除此之外还可以实现UserDetailsService接口来实现自定义用户名密码

配置类

@Configuration
public class SecurityConfiguration {

    @Bean
    public PasswordEncoder getPasswordEncode() {
        return new BCryptPasswordEncoder();
    }
}

使用BCryptPasswordEncoder来对密码进行加密

UserDetailsService实现类

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Value("${application.customize.username:admin}")
    private String customizeUsername;

    @Value("${application.customize.password:123}")
    private String customizePassword;

    @Value("${application.customize.permission.list:admin,normal}")
    private String permissionList;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户名是否在数据存在,如果不存在抛出异常
        if (!customizeUsername.equals(username)) {
            throw new UsernameNotFoundException("用户名不存在: {}" + username);
        }
        // 如果查询到密码的密码进行解析,或者总结把密码传入构造方法
        String password = passwordEncoder.encode(customizePassword);
        // 使用AuthorityUtils构造权限列表
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList(permissionList));
    }
}

注意:最忌代码里写死字符串,尽可能的进行配置

配置文件

application:
  customize:
    username: admin
    password: 123
  permission:
    list: admin,normal

自定义登录页

在resources文件夹下创建一个static文件夹,同时创建login.htmlerror.htmlmain.html页面。在SecurityConfiguration中继承WebSecurityConfigurerAdapter并重写configure(HttpSecurity httpSecurity)方法

配置类代码如下

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 将Spring Security默认的登录页面路由到自定义的登录页面.
     * 由于重写了configure方法,所以需要<code>httpSecurity.authorizeRequests()
     * .anyRequest().authenticated();</code>来进行所有请求的拦截验证。
     * 同时需要对login.html进行放行不需要验证。同时对/login请求进行
     * 拦截,登录成功后会跳转到main.html,登录失败跳转到error.html。
     * 同时需要关闭csrf防护.
     * @param httpSecurity
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/login.html")
                .successForwardUrl("/toMain")
                .failureForwardUrl("/toError");

        httpSecurity.authorizeRequests()
                .antMatchers("/error.html")
                .permitAll()
                .antMatchers("/login.html")
                .permitAll()
                .anyRequest()
                .authenticated();

        httpSecurity.csrf().disable();
    }

    @Bean
    public PasswordEncoder getPasswordEncode() {
        return new BCryptPasswordEncoder();
    }
}

注意:在一些前后端分离的项目中,这种配置是无法实现对应的需求

前后端分离自定义登录页

实现接口AuthenticationSuccessHandler并实现接口的中方法,将登录成功后的页面重定向到对应的url

代码示例

public class SecurityAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private String url;

    public SecurityAuthenticationSuccessHandler(String url) {
        this.url = url;
    }

    /**
     * 前后端分离进行重定向
     * @param httpServletRequest
     * @param httpServletResponse
     * @param authentication
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        Authentication authentication) throws IOException, ServletException {
        httpServletResponse.sendRedirect(url);
    }
}

登录失败就实现接口AuthenticationFailureHandler同时实践其中的方法

代码示例

public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private String url;

    public SecurityAuthenticationFailureHandler(String url) {
        this.url = url;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.sendRedirect(url);
    }
}

配置类

继承WebSecurityConfigurerAdapter重写configure(HttpSecurity httpSecurity)方法

	@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/login.html")
//                .successForwardUrl("/toMain")
                // 登陆成功重定向到https://www.baidu.com
                .successHandler(new SecurityAuthenticationSuccessHandler("https://www.baidu.com"))
//                .failureForwardUrl("/toError");
                // 登录失败重定向到error.html
                .failureHandler(new SecurityAuthenticationFailureHandler("/error.html"));
        httpSecurity.authorizeRequests()
                .antMatchers("/error.html")
                .permitAll()
                .antMatchers("/login.html")
                .permitAll()
                .anyRequest()
                .authenticated();

        httpSecurity.csrf().disable();
    }

antMatcher

方法定义

public C antMatchers(String... antPatterns)

参数是不定向参数,每个参数是一个ant表达式,用于匹配url

规则如下:

1、?:匹配一个字符

2、*:匹配0个或多个字符

3、**:匹配0个或多个目录

在实际项目中经常需要放行所有的静态资源,如下

.antMatchers("/js/**","/css/**").permitAll()

还有一种配置方式是只是.js文件都放行

.antMatchers("/**/*.js").permitAll()

regexMatchers

方法定义

public C regexMatchers(String... regexPatterns)

regexMatchers使用的是正则表达式来匹配对应的资源进行权限校验。除此之外还有通过HttpMethod来限定匹配的请求类型,如下

httpSecurity.authorizeRequests()
                .regexMatchers(HttpMethod.POST,"/demo")
                .permitAll()
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

mvcMatchers

方法定义

public ExpressionUrlAuthorizationConfigurer<H>.MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) 

mvcMatchers是用来匹配在配置文件中配置的servlet.path,如下

spring:
  mvc:
    servlet:
      path: /security
httpSecurity.authorizeRequests()
                .mvcMatchers("/hello")
                .servletPath("/security")
                .permitAll()
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

权限控制

权限控制一般需要实现UserDetailsService接口中的loadUserByUsername方法,并在其中设置需要的权限列表,如下

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户名是否在数据存在,如果不存在抛出异常
        if (!customizeUsername.equals(username)) {
            throw new UsernameNotFoundException("用户名不存在: {}" + username);
        }
        // 如果查询到密码的密码进行解析,或者总结把密码传入构造方法
        String password = passwordEncoder.encode(customizePassword);
        // 使用AuthorityUtils构造权限列表
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList(permissionList));
    }

其中permissionList就是权限列表,我使用的是@Value注解进行注入,如下

@Value("${application.customize.permission.list:admin,normal}")
private String permissionList;

配置类

在配置类中继承WebSecurityConfigurerAdapter重写configure方法,可以对其进行权限控制。如admin.html页面只能admin进行访问,那么配置如下:

httpSecurity.authorizeRequests()
                .antMatchers("/admin.html")
                .hasAnyAuthority("admin")
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

其中admin就是在配置类中的权限列表

角色配置

同上编写service实现类并继承UserDetailsService中的loadUserByUsername方法,并配置权限列表,但权限列表配置如下

@Value("${application.customize.permission.list:admin,normal,ROLE_hello}")
private String permissionList;

其中的ROLE_hello就代表角色配置,注意一定要以ROLE_开头,否则Spring Security无法识别。

配置类

 httpSecurity.authorizeRequests()
                .antMatchers("/role.html")
                .hasAnyRole("hello")
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

这里使用的是hasAnyRole进行角色控制

IP地址判断

这里的ip地址指的是用户浏览器输入的地址,比如localhost是本机的地址,也可以是127.0.0.1,也可以是Windows cmd的ipconfig命令的ip地址

配置类

httpSecurity.authorizeRequests()
                .antMatchers("/main.html")
                .hasIpAddress("127.0.0.1")
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

这里配置的127.0.0.1只能在浏览器输入127.0.0.1才有效,输入localhostWindows cmd的ipconfig命令的ip地址均无法访问。

自定义403处理

编写自定义类SecurityAccessDeniedHandler同时实现接口AccessDeniedHandler,实现handle方法

Handler配置

@Component
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {

    /**
     * 设置响应的状态码,处理403状态码请求,同时
     * 设置请求头,并返回json字符串
     * @param httpServletRequest
     * @param httpServletResponse
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void handle(HttpServletRequest httpServletRequest,
                       HttpServletResponse httpServletResponse,
                       AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        @Cleanup PrintWriter writer = httpServletResponse.getWriter();
        writer.write("{\"status\": \"error\", \"message\": \"权限不足请联系管理员\"}");
        writer.flush();
    }
}

编写配置类SecurityConfiguration继承WebSecurityConfigurerAdapter重写configure方法

配置类

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.formLogin()
        .loginProcessingUrl("/login")
        .loginPage("/login.html")
        .successHandler(new SecurityAuthenticationSuccessHandler("/main.html"))
        // 登录失败重定向到error.html
        .failureHandler(new SecurityAuthenticationFailureHandler("/error.html"));
    httpSecurity.authorizeRequests()
        // 可以写多条对不同的url进行权限认证
        .antMatchers("/error.html")
        // 允许访问的权限
        .permitAll()
        .antMatchers("/login.html")
        .permitAll()
        // 所有请求都会被认证
        .anyRequest()
        .authenticated();

    httpSecurity.csrf().disable();
    httpSecurity.exceptionHandling()
        .accessDeniedHandler(securityAccessDeniedHandler);
}

httpSecurity.exceptionHandling().accessDeniedHandler(securityAccessDeniedHandler);设置自定义的AccessDeniedHandler

基于表达式的访问控制

Access方法使用

登录用户权限判断实际上底层实现都是调用access(表达式)

表达式描述
hasRole(String role)如果当前主体具有指定角色,则返回true。例子:hasRole('admin'),默认情况下,如果提供的角色不以 开头ROLE_,则会添加它。defaultRolePrefix您可以通过修改on来自定义此行为DefaultWebSecurityExpressionHandler
hasAnyRole(String… roles)如果当前主体具有任何提供的角色(以逗号分隔的字符串列表形式给出),则返回true。例子:hasAnyRole('admin', 'user')
hasAuthority(String authority)如果当前委托人具有指定的权限,则返回true
hasAnyAuthority(String… authorities)如果当前主体具有任何提供的权限(以逗号分隔的字符串列表形式给出),则返回true。例子:hasAnyAuthority('read', 'write')
principal允许直接访问代表当前用户的主体对象。
authentication允许直接访问AuthenticationSecurityContext.
permitAll始终评估为true.
denyAll始终评估为false.
isAnonymous()如果当前主体是匿名用户则返回true
isRememberMe()如果当前主体是记住我的用户,则返回true
isAuthenticated()如果用户不是匿名用户则返回true
isFullyAuthenticated()如果用户不是匿名用户且不是记住我的用户,则返回true
hasPermission(Object target, Object permission)如果用户有权访问给定权限的提供目标,则返回true。例如,hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission)如果用户有权访问给定权限的提供目标,则返回true。例如,hasPermission(1, 'com.example.domain.Message', 'read')

Access自定义权限控制

编写自定义接口以及自定义方法

public interface SecurityService {
    Boolean hasPermission(HttpServletRequest httpServletRequest,
                                  Authentication authentication);
}

编写对应的实现类,注意要注册为Spring的一个Bean

@Service
public class SecurityServiceImpl implements SecurityService {

    @Override
    public Boolean hasPermission(HttpServletRequest httpServletRequest,
                                 Authentication authentication) {
        // 获取主体
        Object object = authentication.getPrincipal();
        if (object instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) object;
            // 获取权限
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            // 判断权限是否存在对应的URI
            return authorities.contains(new SimpleGrantedAuthority(httpServletRequest.getRequestURI()));
        }
        return false;
    }
}

通过获取主体以及对应的URI来编写对应的权限

通过httpSecurity.authorizeRequests()..anyRequest().access("");方法来调用自定义的SecurityServiceImpl中的自定义hasPermission方法

配置类

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/login.html")
                .successHandler(new SecurityAuthenticationSuccessHandler("/main.html"))
                // 登录失败重定向到error.html
                .failureHandler(new SecurityAuthenticationFailureHandler("/error.html"));
        httpSecurity.authorizeRequests()
                // 可以写多条对不同的url进行权限认证
                .antMatchers("/error.html")
                // 允许访问的权限
                .permitAll()
                .antMatchers("/login.html")
                .permitAll()
                .anyRequest()
                // 注意是beanName不是类名
                .access("@securityServiceImpl.hasPermission(request, authentication)");

        httpSecurity.csrf().disable();
        httpSecurity.exceptionHandling()
                    .accessDeniedHandler(securityAccessDeniedHandler);
    }

自定义配置类继承WebSecurityConfigurerAdapter类重写configure方法。值得注意的是在登录成功后会跳转到main.html页面,但由于自定义的Access中没有main.thml所以登录成功后会403

解决方法

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Value("${application.customize.username:admin}")
    private String customizeUsername;

    @Value("${application.customize.password:123}")
    private String customizePassword;

        			    @Value("${application.customize.permission.list:admin,normal,ROLE_hello,/main.html}")
    private String permissionList;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户名是否在数据存在,如果不存在抛出异常
        if (!customizeUsername.equals(username)) {
            throw new UsernameNotFoundException("用户名不存在: {}" + username);
        }
        // 如果查询到密码的密码进行解析,或者总结把密码传入构造方法
        String password = passwordEncoder.encode(customizePassword);
        // 使用AuthorityUtils构造权限列表
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList(permissionList));
    }
}

permissionList加入/main.html即可

基于注解的访问控制

Spring Security中提供了一些访问控制的注解。这些注解都是默认不可用的,需要通过@EnableGlobalMethodSecurity进行开启后使用

如果设置条件允许,程序正常运行,如果不允许会报500,如下

org.springframework.security.access.AccessDeniedException:不允许访问

这些注解可以写到Service接口或方法上也可以写到ControllerController的方法上。通常情况下都是卸载控制器方法上。控制器接口URL是否允许被访问

@Secured注解

@Secured是专门用于判断是否具有角色的。能写在方法或类上。参数以ROLE_开头

启动类添加@EnableGlobalMethodSecurity(securedEnabled = true)

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityStudyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityStudyApplication.class, args);
    }
}

注意这里的配置类不能配置successHandler以及failureHandler

接口Controller添加@Secured

@Secured("ROLE_hello")
@PostMapping("/toMain")
public String toMain() {
    return "redirect:main.html";
}

即角色为hello的用户才可以访问该接口,角色由实现接口UserDetailsService重写loadUserByUsername方法中即可实现

@PreAuthorize以及@PostAuthorize

@PreAuthorize@PostAuthorize都是方法或类级别的注解

@PreAuthorize:表示访问方法或类在执行之前判断权限,大多数情况下都是使用这个注解,注解的参数和access()方法参数取值相同,都是权限表达式

@PostAuthorize:表示方法或类执行结束后判断权限,该注解很少被使用

启动类添加@EnableGlobalMethodSecurity(prePostEnabled = true),接口Controller添加@PreAuthorize

@PreAuthorize("hasRole('world')")
@PostMapping("/toMain")
public String toMain() {
    return "redirect:main.html";
}

注意:同样在配置类中的configure方法不能配置successHandler以及failureHandler

RememberMe

Spring Security中的RememberMe,用户只需要在登录时添加remember me复选框,取值为true,即可将用户信息存储到数据库中,以后就可以不登录访问了

添加依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

application.yml配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/security?userUnicode=true&charseterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456

注入PersistentTokenRepository

@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    // 自动建表,第一次启动需要,第二次启动需要启动
    //        jdbcTokenRepository.setCreateTableOnStartup(true);
    return jdbcTokenRepository;
}

继承WebSecurityConfigurerAdapter类,重写configure方法的配置如下:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.formLogin()
        .loginProcessingUrl("/login")
        .loginPage("/login.html")
        .successForwardUrl("/toMain")
        .failureForwardUrl("/toError");
    httpSecurity.authorizeRequests()
        // 可以写多条对不同的url进行权限认证
        .antMatchers("/error.html")
        // 允许访问的权限
        .permitAll()
        .antMatchers("/login.html")
        .permitAll()
        .anyRequest()
        .authenticated();

    httpSecurity.csrf().disable();
    httpSecurity.exceptionHandling()
        .accessDeniedHandler(securityAccessDeniedHandler);

    httpSecurity.rememberMe()
        .userDetailsService(userDetailServiceImpl)
        // 持久层对象
        .tokenRepository(persistentTokenRepository);
}

前端HTML页面修改如下

<form action="/login" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    记住我<input type="checkbox" name="remember-me" value="true"><br/>
    <input type="submit" value="登录"/>
</form>

注意:这里的name一定是remember-mevaluetrue。登录后在数据库的表里会发现录入有一条数据,该失效时间默认是两周,修改默认失效时间,以及remember-me的别名如下:

httpSecurity.rememberMe()
    // 失效时间,单位:秒
    .tokenValiditySeconds(60)
    // 设置remember-me的别名
    .rememberMeParameter("remember-me-alias")
    .userDetailsService(userDetailServiceImpl)
    // 持久层对象
    .tokenRepository(persistentTokenRepository);

CSRF

CSRF(Cross-site request forgery)跨站请求伪造,也称为"OneClick Attack"或者Session Riding。通过伪造用户请求访问受信任站点的非法请求

跨域:只要网络协议、IP地址、端口中任何一个不相同就是跨域请求

客户端与服务进行交互时,由于Http协议是无状态的协议,所以引入了Cookie进行记录客户端身份。在Cookie中会存放Session ID用来识别客户端的身份。在跨域情况下,session id可能被第三方恶意劫持,通过这个Session id向服务器发起请求时,服务端会认为该请求是合法的,可能发生很多意想不到的事情。

Spring Security中的CSRF

从Spring Security4开始CSRF防护默认开启,默认会拦截请求。进行CSRF处理。CSRF为了保证不是其他第三方网站访问,要求访问时同时携带参数名为_csrf值为token(token在服务器产生的)的内容,如果token和服务器的token匹配成功,则访问成功。

示例

引入thymeleaf依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

配置类

配置类继承WebSecurityConfigurerAdapter重写configure方法,并放行对应的请求

protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/showLogin")
                .successForwardUrl("/toMain")
                .failureForwardUrl("/toError");
        httpSecurity.authorizeRequests()
                // 可以写多条对不同的url进行权限认证
                .antMatchers("/error.html")
                // 允许访问的权限
                .permitAll()
                .antMatchers("/showLogin")
                .permitAll()
                // 所有请求都会被认证
                .anyRequest()
                .authenticated();

//        httpSecurity.csrf().disable();
        httpSecurity.exceptionHandling()
                    .accessDeniedHandler(securityAccessDeniedHandler);

        httpSecurity.rememberMe()
                    // 失效时间,单位:秒
                    .tokenValiditySeconds(60)
                    .userDetailsService(userDetailServiceImpl)
                    // 持久层对象
                    .tokenRepository(persistentTokenRepository);
        httpSecurity.logout()
                    .logoutSuccessUrl("/logout");
    }

关闭Spring Security CSRF

Controller跳转到视图

@RequestMapping("/showLogin")
public String showLogin() {
    return "login";
}

该视图跳转到resources/templates/login.html下的页面

thymeleaf页面

<form action="/login" method="post">
    <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/>
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    记住我<input type="checkbox" name="remember-me" value="true"><br/>
    <input type="submit" value="登录"/>
</form>

<input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/>是表单提交时携带_csrf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/402869.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于java的网络选课商城项目部署

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

SAP 生产订单收货入库Goods Receipt

Goods Receipt: 收货这块比较简单&#xff0c;当我们做完报工之后&#xff0c;成品就可以入库了。 那么收货完了&#xff0c;到底会有什么样的影响呢&#xff1f; 会产生物料凭证以及会计凭证&#xff0c;但是若订单中勾选”GR非股价的“&#xff0c;则不会有价值。 这里我们需…

E1. Unforgivable Curse (easy version) #855 div3

ps&#xff1a;很久没有更新啦&#xff0c;之前一直在复习准备期末考试&#xff0c;也没怎么写题。现在考完要恢复训练啦 Problem - E1 - Codeforces 题意&#xff1a; 两个字符串s和t&#xff0c;在s中任意两个间隔为k或者k1的字母可以进行任意次的交换&#xff0c;问你可不…

STL源码剖析(1) - 空间配置器与内存操作详解

文章首发于&#xff1a;My Blog 欢迎大佬们前来逛逛1. SGI空间配置器SGI STL的空间配置器是 alloc而非allocator&#xff0c;并且不接受任何参数&#xff1a;vector<int,std::alloc> vec我们通常使用缺省的空间配置器&#xff1a;template <typename T,typename Alloc…

mac 安装python、pip、weditor

问题现象&#xff1a;执行 python3 -m weditor 报错 ➜ ~ python3 -m weditor dyld[42143]: dyld cache (null) not loaded: syscall to map cache into shared region failed dyld[42143]: Library not loaded: /System/Library/Frameworks/CoreFoundation.framework/Versio…

【前端vue2面试题】2023前端最新版vue2模块,高频24问

​ &#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;博主收集的关于vue2面试题 目录 vue2面试题 1、$route 和 $router的区别 2、一个.v…

Redis高频面试题汇总(上)

目录 1.什么是Redis? 2.为什么Redis这么快 3.分布式缓存常见的技术选型方案有哪些&#xff1f; 4.你知道 Redis 和 Memcached 的区别吗&#xff1f; 5.Redis使用场景有哪些 6.Redis 常用的数据结构有哪些&#xff1f; 7.Redis 数据类型有哪些底层数据结构&#xff1f; …

sonarqube指标详解

最近公司引入了sonar&#xff0c;作为代码质量检测工具&#xff0c;以期提高研发同学的代码质量&#xff0c;但是结果出来后&#xff0c;有些同学不清楚相应的指标内容&#xff0c;不知道应该重点关注哪些指标&#xff0c;于是查询了一下相关的资料&#xff0c;加以总结同时也分…

【数据结构】堆排序

堆是一种叫做完全二叉树的数据结构&#xff0c;可以分为大根堆&#xff0c;小根堆&#xff0c;而堆排序就是基于这种结构而产生的一种程序算法。大堆&#xff1a;每个节点的值都大于或者等于他的左右孩子节点的值小堆&#xff1a;每个结点的值都小于或等于其左孩子和右孩子结点…

扬帆优配|业务量大突破,这个行业发展明显向好

近期上市的新股&#xff0c;大都在招股阐明书里公布了本年第一季度成绩预告。 我国快递事务量本年已达200亿件 国家邮政局监测数据显现&#xff0c;到3月8日&#xff0c;本年我国快递事务量已到达200.9亿件&#xff0c;比2019年到达200亿件提前了72天&#xff0c;比2022年提前…

goland开发环境搭建及运行第一个go程序HelloWorld

1、下载和安装golang 点击进入下载页面 下载好安装包&#xff0c;点击安装。 我之前安装过低版本的安装包&#xff0c;所以这里提示要先卸载已经安装过的低版本的。 同意协议&#xff0c;继续安装。 默认安装的文件夹为C盘&#xff0c;建议更改&#xff0c;我这里更改为D盘…

YOLOv5训练大规模的遥感实例分割数据集 iSAID从切图到数据集制作及训练

最近想训练遥感实例分割&#xff0c;纵观博客发现较少相关 iSAID数据集的切分及数据集转换内容&#xff0c;思来想去应该在繁忙之中抽出时间写个详细的教程。 iSAID数据集下载 iSAID数据集链接 下载上述数据集。 百度网盘中的train和val中包含了实例和语义分割标签。 上述…

哪些职业适合创业?学习哪些技能可以自己创业?

创意行业&#xff1a;创意行业包括广告、设计、影视等领域&#xff0c;需要创新思维和创意能力&#xff0c;适合创业。学习创意思维、平面设计、影视制作等技能可以自己创业。 科技行业&#xff1a;科技行业包括互联网、人工智能、物联网等领域&#xff0c;需要技术能力和创新思…

基于JavaEE开发博客系统项目开发与设计(附源码)

文章目录1.项目介绍2.项目模块3.项目效果1.项目介绍 这是一个基于JavaEE开发的一个博客系统。实现了博客的基本功能&#xff0c;前台页面可以进行文章浏览&#xff0c;关键词搜索&#xff0c;登录注册&#xff1b;登陆后支持对文章进行感谢、评论&#xff1b;然后还可以对评论…

[网络工程师]-网络规划与设计-逻辑网络设计(二)

3、广域网技术选择 3.1广域网互连技术 3.1.1 数字数据网络 数字数据网络(Digital Data Network,DDN)是一种利用数字信道提供数据信号传输的数据传输网,是一个半永久性连接电路的公共数字数据传输网络,为用户提供了一个高质量、高带宽的数字传输通道。 利用DDN网络实现局…

【C++】7.string

1.标准库的string类 string是表示字符串的字符串类在使用string类时&#xff0c;必须包含#include头文件以及using namespace std;string类是使用char(即作为它的字符类型&#xff0c;使用它的默认char_traits和分配器类型(关于模板的更多信息&#xff0c;请参阅basic_string)…

智能网联汽车安全芯片介绍(一)

汽车的新四化(电动化、网联化、智能化、共享化)让汽车安全越来越受到重视,比如一个不太容易被破解的汽车遥控钥匙或者非接触开门等,越智能越开始需要安全。而过去的一些安全事件也凸显了安全的必要性。 黑客早已经盯上了汽车。2015年,Charlie Miller 、 Chris Valsek曾通过…

熟悉mmdetection3d数据在模型中的处理流程

目录1、搭建模型2、读取数据3、运行流程3.1 图像特征获取3.2 点云特征获取3.3 head3.4 编码bbox4、可视化5、总结本图文数据集采取KITTI数据集配置文件的介绍可以参考博主上一篇图文本图文旨在利用一条数据&#xff0c;走完整个多模态数据处理分支&#xff0c;获得bbox&#xf…

Linux内核里的传输层数据流

传输层发送数据包socket sendto系统调用应用程序通过socket调用sendto系统调用发送数据包&#xff0c;根据网络层协议调用inet_sendmsg或者inet6_sendmsg()函数&#xff0c;将数据包送入协议栈发送。SYSCALL_DEFINE6(sendto...) - net/socket.csock_sendmsg() - net/socket.cso…

compose系列教程-2. 显示图片

要在Android中使用Compose显示图片&#xff0c;需要使用Image组件。以下是一个简单的例子&#xff0c;它显示了一张图片&#xff1a; Composable fun MyApp() { val image painterResource(id R.drawable.my_image) Image(painter image, contentDescription "…