当前位置: 首页 > news >正文

Spring Security之初体验

目录

目标

版本

初步集成Security

配置密码和账号权限

自定义登录页面

通过Spring Security默认的表进行认证和授权

自定义表进行认证和授权

设置密码加密

设定登录超时时间

隐藏登录页面


目标

        将Spring Security集成到SpringBoot,通过配置给不同用户授权。本文适合初学者去了解Spring Security的基本应用,且部分内容只适用于前后不分离的项目


版本

        <spring-boot.version>2.6.13</spring-boot.version>


初步集成Security

步骤

第一步:搭建SpringBoot项目。为了后面实现动态授权,我这里把MyBatis、MySQL也集成到了项目中,以下是Maven依赖。

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

第二步:给启动类加上@EnableWebSecurity注解。

package com.ctx;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;@SpringBootApplication
@EnableWebSecurity
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}

第三步:配置application配置文件。

server.port=8010spring.datasource.url=jdbc:mysql://127.0.0.1:3306/school?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivermybatis.mapper-locations=classpath:/mapper/*.xml

第四步:写一个测试接口。

package com.ctx.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MyController {/*** http://localhost:8010/fun* @return*/@GetMapping("/fun")public String fun(){return "fun";}/*** http://localhost:8010/fun2* @return*/@GetMapping("/fun2")public String fun2(){return "fun2";}
}

第五步:启动项目并调用接口,发现跳转到了登录页面。

第六步:看控制台打印的信息,里面有user用户的密码,每次重启项目user用户的密码都会变化。


配置密码和账号权限

步骤

第一步:创建一个子类继承WebSecurityConfigurerAdapter类,作用是自定义web安全规则。

package com.ctx.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {//关闭csrf跨域检查http.csrf().disable().authorizeRequests()//匹配路径 /fun/** 的请求,要求用户必须拥有 "fun" 权限.antMatchers("/fun/**").hasAuthority("fun")//匹配路径 /fun2/** 的请求,要求用户必须拥有 "fun2" 权限.antMatchers("/fun2/**").hasAuthority("fun2")//匹配路径 /common/** 的请求,允许所有用户访问,不需要登录.antMatchers("/common/**").permitAll()//所有其他请求都需要认证(登录).anyRequest().authenticated().and() //结束当前配置,继续下一个配置模块//配置表单登录成功后的默认跳转页面.formLogin().defaultSuccessUrl("/index.html")//登录失败后的跳转页面.failureUrl("/failure.html").and()//权限不足则跳转到该页面.exceptionHandling().accessDeniedPage("/accessDenied.html");}
}

第二步:创建一个配置类实现WebMvcConfigurer,配置用户登录信息。

package com.ctx.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("redirect:/login");}/*** PasswordEncoder认证时的密码比对* @return*/@Beanpublic PasswordEncoder getPasswordEncoder() {//return new BCryptPasswordEncoder(10);//把用户输入的密码直接与正确的密码进行对比,没有加密解密过程。不推荐使用。return NoOpPasswordEncoder.getInstance();}/*** 用户在表单中输入用户名和密码时,Spring Security会调用我们配置的UserDetailsService加载用户数据。* @return*/@Beanpublic UserDetailsService getUserDetailsService() {return new InMemoryUserDetailsManager(//分别指用户、密码、角色、权限User.withUsername("zhangsan").password("123456").roles("teacher").authorities("fun").build(),User.withUsername("lisi").password("123456").roles("student").authorities("fun2").build(),User.withUsername("root").password("123456").roles("root").authorities("fun","fun2").build());}
}

第三步:创建一些html静态页面,方便更直观地演示认证授权效果。

index.html

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>登录成功 - 功能选择</title>
</head>
<body>
<h1>欢迎,已登录</h1>
<p>请选择要访问的功能:</p><button onclick="location.href='http://localhost:8010/fun'">访问功能 fun</button>
<button onclick="location.href='http://localhost:8010/fun2'">访问功能 fun2</button>
<button onclick="location.href='http://localhost:8010/logout'">退出登录</button>
</body>
</html>

failure.html

<!-- failure.html -->
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>登录失败</title>
</head>
<body>
<h1 style="color:red;">登录失败</h1>
<p>请检查用户名和密码是否正确。</p>
<a href="/login">返回登录</a>
</body>
</html>

accessDenied.html

<!-- 403.html -->
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>403 - 无权限访问</title>
</head>
<body>
<h1 style="color: red;">权限不足</h1>
<p>你没有权限访问该资源。</p>
<a href="/index.html">返回首页</a>
</body>
</html>

第四步:启动项目,分别用zhangsan、lisi、root去访问两个接口。发现每个账号的权限果真如我们配置的一般。


自定义登录页面

步骤

第一步:考虑到后面css、js、图片元素,所以我们重新规划好前端资源的目录结构。把我们之前的html配置页面前都加上/html前缀。

第二步:定义一个html首页。为了更好地演示授权控制,我们可以把首页做得复杂一点,加入css、js、图片元素。

homePage.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>登录</title><link rel="stylesheet" href="/css/homePage.css" />
</head>
<body>
<div class="login-container"><img src="/img/logo.png" alt="Logo" class="logo"/><!-- 使用 Spring Security 默认登录接口 --><form action="/login" method="post"><h2>Login</h2><div class="input-group"><label for="username">Username</label><input type="text" id="username" name="username" required /></div><div class="input-group"><label for="password">Password</label><input type="password" id="password" name="password" required /></div><button type="submit">Login</button></form><!-- 可选的错误消息提示 --><p class="message" id="message"><!-- 你也可以用 Spring Security 的 /login?error 来显示错误 --><!-- 可通过 JS 或 Thymeleaf 动态渲染错误 --></p>
</div><script src="/js/homePage.js"></script>
</body>
</html>

homePage.css

/* homePage.css */
body {margin: 0;padding: 0;font-family: Arial, sans-serif;background: linear-gradient(135deg, #4e54c8, #8f94fb);height: 100vh;display: flex;align-items: center;justify-content: center;
}.login-container {background: white;padding: 30px 40px;border-radius: 10px;box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);text-align: center;width: 300px;
}.logo {width: 80px;margin-bottom: 20px;
}.input-group {margin-bottom: 15px;text-align: left;
}.input-group label {display: block;font-weight: bold;margin-bottom: 5px;
}.input-group input {width: 100%;padding: 8px;border: 1px solid #ccc;border-radius: 5px;
}button {background: #4e54c8;color: white;padding: 10px;width: 100%;border: none;border-radius: 5px;font-size: 16px;cursor: pointer;
}button:hover {background: #3b3fc1;
}.message {margin-top: 15px;color: red;
}

homePage.js

// script.js
document.getElementById("loginForm").addEventListener("submit", function (e) {e.preventDefault();const username = document.getElementById("username").value.trim();const password = document.getElementById("password").value.trim();const message = document.getElementById("message");if (username === "admin" && password === "123456") {message.style.color = "green";message.textContent = "Login successful!";} else {message.style.color = "red";message.textContent = "Invalid username or password.";}
});

logo.png

第三步:在WebSecurityConfig类中调整登录页面、登录页面权限、退出登录逻辑。

package com.ctx.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {// 关闭 CSRF 跨域检查(适用于前端静态页面,生产建议打开)http.csrf().disable().authorizeRequests()// 路径权限控制.antMatchers("/fun/**").hasAuthority("fun").antMatchers("/fun2/**").hasAuthority("fun2").antMatchers("/common/**", "/html/homePage.html", "/css/**", "/js/**", "/img/**").permitAll().anyRequest().authenticated().and()// 配置登录逻辑.formLogin().loginPage("/html/homePage.html") // 指定登录页面(你自己的 HTML).loginProcessingUrl("/login")     // Spring Security 登录接口(表单 action 的地址).defaultSuccessUrl("/html/index.html") // 登录成功跳转.failureUrl("/html/failure.html")      // 登录失败跳转.and()// 配置退出逻辑.logout().logoutUrl("/logout")                    // 默认就是 /logout,可以省略.logoutSuccessUrl("/html/homePage.html") // 退出成功跳转页面(你的登录页).invalidateHttpSession(true)             // 注销 session.deleteCookies("JSESSIONID")             // 删除 cookie,确保彻底退出.and()// 权限不足跳转页面.exceptionHandling().accessDeniedPage("/html/accessDenied.html");}
}

第四步:配置WebConfig类的addViewControllers,设置我们自定义的首页。

    @Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("forward:/html/homePage.html");}

第五步:启动项目并访问测试(略)。


通过Spring Security默认的表进行认证和授权

需求

        生产环境中的账号和权限信息保存在数据库中,而非我们上述代码那样通过硬编码把账号和权限信息保存在内存中。接下来我们要把这些信息保存在数据库中,并实现认证和授权。

步骤

第一步:修改WebConfig类的getUserDetailsService方法。DataSource作为该方法的参数,Spring Boot会根据application配置文件把数据源注入进去。

    @Beanpublic UserDetailsService getUserDetailsService(DataSource dataSource) {return new JdbcUserDetailsManager(dataSource);}

第二步:创建Spring Security默认的两张表,我们可以在官方文档找到它。这里注意,要把varchar_ignorecase改成varchar类型

CREATE TABLE users(username VARCHAR(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR(500) NOT NULL,enabled BOOLEAN NOT NULL
);CREATE TABLE authorities (username VARCHAR(50) NOT NULL,authority VARCHAR(50) NOT NULL,CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username)
);
CREATE UNIQUE INDEX ix_auth_username ON authorities (username,authority);

Spring Security JDBC身份验证https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/jdbc.html

第三步:往这两张表中写入数据。

-- 用户 zhangsan(角色 teacher,权限 fun)
INSERT INTO users (username, PASSWORD, enabled) VALUES
('zhangsan', '123456', TRUE);INSERT INTO authorities (username, authority) VALUES
('zhangsan', 'ROLE_teacher'),
('zhangsan', 'fun');-- 用户 lisi(角色 student,权限 fun2)
INSERT INTO users (username, PASSWORD, enabled) VALUES
('lisi', '123456', TRUE);INSERT INTO authorities (username, authority) VALUES
('lisi', 'ROLE_student'),
('lisi', 'fun2');-- 用户 root(角色 root,权限 fun, fun2)
INSERT INTO users (username, PASSWORD, enabled) VALUES
('root', '123456', TRUE);INSERT INTO authorities (username, authority) VALUES
('root', 'ROLE_root'),
('root', 'fun'),
('root', 'fun2');

第四步:启动项目并测试(略)。


自定义表进行认证和授权

需求

        项目中的用户和权限信息表往往和Spring Security默认的表不一样,请将项目中的用户和权限信息表与Spring Security进行整合,实现认证和授权功能。

分析

        用户在表单中输入用户名和密码时,Spring Security会调用我们配置的UserDetailsService加载用户数据。我们之前的代码中,WebConfig类已经对UserDetailsService做了一些修改,并将它交给了Spring容器进行管理。我们进入到UserDetailsService内部,发现它是一个接口,并且只有一个方法。该方法的作用是:根据用户名查询并返回用户信息。我们可以实现这个接口,使得项目中的用户表与之关联。

第一步:自定义用户表和权限表。

CREATE TABLE `my_user` (`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`user_password` varchar(64) DEFAULT NULL,PRIMARY KEY (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `my_authorities` (`id` int NOT NULL AUTO_INCREMENT,`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`authority` varchar(50) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ix_auth_username` (`user_name`,`authority`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE UNIQUE INDEX ix_auth_username ON `my_authorities` (user_name,authority);

第二步:插入一些用户和权限数据。

INSERT INTO `my_user`(user_name,user_password)VALUES
("zhangsan","123456"),
("lisi","123456"),
("wangwu","123456"),
("root","123456")
INSERT INTO `my_authorities`(user_name,authority)VALUES
("zhangsan","fun"),
("zhangsan","ROLE_teacher"),("lisi","fun2"),
("lisi","ROLE_student"),("root","fun"),
("root","fun2"),
("root","ROLE_root")

第三步:实现UserDetailsService接口,并将我们刚才创建的两张表与之关联。

package com.ctx.config;import com.ctx.dao.MyUserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.CollectionUtils;import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class MyUserService implements UserDetailsService {@Autowiredprivate MyUserDao myUserDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Map<String, Object> userMap = myUserDao.loadUserByUsername(username);if (userMap == null) {throw new UsernameNotFoundException("用户不存在:" + username);}List<String> authorityList = myUserDao.selectAuthorityByUsername(username);//User.UserBuilder userBuilder = User.withUsername(username).password(userMap.get("userPassword").toString());//String[] array = authorityList.toArray(new String[authorityList.size()]);if (CollectionUtils.isEmpty(authorityList)) {throw new UsernameNotFoundException("用户:" + username+"没有任何权限。");}userBuilder.authorities(array);return userBuilder.build();}
}
package com.ctx.dao;import org.apache.ibatis.annotations.Param;import java.util.List;
import java.util.Map;@org.apache.ibatis.annotations.Mapper
public interface MyUserDao {Map<String, Object> loadUserByUsername(@Param("username") String username);List<String> selectAuthorityByUsername(@Param("username") String username);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.ctx.dao.MyUserDao"><select id="loadUserByUsername" resultType="java.util.Map">SELECTuser_name userName,user_password userPassword,FROMmy_userWHEREuser_name = #{username}</select><select id="selectAuthorityByUsername" resultType="java.util.Map">SELECT`authority`FROM`my_authorities`WHEREuser_name = #{username}</select></mapper>
package com.ctx.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.sql.DataSource;@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("forward:/html/homePage.html");}/*** PasswordEncoder认证时的密码比对* @return*/@Beanpublic PasswordEncoder getPasswordEncoder() {//return new BCryptPasswordEncoder(10);//把用户输入的密码直接与正确的密码进行对比,没有加密解密过程。不推荐使用。return NoOpPasswordEncoder.getInstance();}/*** 用户在表单中输入用户名和密码时,Spring Security会调用我们配置的UserDetailsService加载用户数据。* @return*/@Beanpublic UserDetailsService getUserDetailsService(DataSource dataSource) {return new MyUserService();}
}

第四步:启动项目并验证(略)。

扩展:org.springframework.security.core.userdetails.User中还有四个属性,这四个属性都要为true才能认证成功。

package com.ctx.config;import com.ctx.dao.MyUserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.CollectionUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class MyUserService implements UserDetailsService {@Autowiredprivate MyUserDao myUserDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Map<String, Object> userMap = myUserDao.loadUserByUsername(username);if (userMap == null) {throw new UsernameNotFoundException("用户不存在:" + username);}List<String> authorityList = myUserDao.selectAuthorityByUsername(username);//List<GrantedAuthority> authorities =null;if (CollectionUtils.isEmpty(authorityList)) {throw new UsernameNotFoundException("该账号没有任何权限:" + username);}else{authorityList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}UserDetails userDetails = User.withUsername(username).password(userMap.get("userPassword").toString()).authorities(authorityList.toArray(new String[0])).accountLocked(false).accountExpired(false).disabled(false).credentialsExpired(false).build();System.out.println("用户名: " + userDetails.getUsername());System.out.println("密码: " + userDetails.getPassword());System.out.println("是否启用: " + userDetails.isEnabled());System.out.println("是否账号未过期: " + userDetails.isAccountNonExpired());System.out.println("是否账号未锁定: " + userDetails.isAccountNonLocked());System.out.println("是否密码未过期: " + userDetails.isCredentialsNonExpired());System.out.println("权限列表: ");userDetails.getAuthorities().forEach(auth -> System.out.println(" - " + auth.getAuthority()));return userDetails;}
}


设置密码加密

需求

        生产环境中,账号的密码是以一串密文的形式存储在数据库中的,修改刚才的认证系统,把数据库中的密码设置成密文存储。

分析

        BCryptPasswordEncoder是Spring Security提供的密码加密工具,常用来对密码进行加密和匹配校验。它基于BCrypt哈希算法,安全性很好,推荐用来存储和验证密码。

第一步:创建加密方法,并对数据库中的密码进行加密,然后替换。

    public static void main(String[] args) {BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();// 加密密码String rawPassword = "123456";String encodedPassword = encoder.encode(rawPassword);System.out.println("加密后的密码:" + encodedPassword);// 验证密码boolean matches = encoder.matches(rawPassword, encodedPassword);System.out.println("密码匹配结果:" + matches);}

第二步:修改getPasswordEncoder方法的密码认证方式。

    @Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}

第三步:启动项目并测试(略)。


设定登录超时时间

需求

        用户登录以后,在一定时间内可以访问权限内的资源,超时后需要重新登录才能继续访问。

方法一

第一步:在application文件中设定以下属性:

#设定登录超时时间是2分钟。注意:2m后面不要带空格
server.servlet.session.timeout=2m

方法二

第一步:实现AuthenticationSuccessHandler接口。

package com.ctx.config;import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;@Component
public class MyAuthenticationSuccessHandler  implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication)throws IOException, ServletException {// 登录成功后设置 session 超时时间HttpSession session = request.getSession(false);if (session != null) {session.setMaxInactiveInterval(60); //秒}// 登录成功后跳转response.sendRedirect("/html/index.html");}
}

第二步:使上面的配置生效。这里注意:defaultSuccessUrl("/html/index.html")与response.sendRedirect("/html/index.html");冲突,需要把它注释掉。

package com.ctx.config;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {private final MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;// Spring自动注入,不需要写@Autowiredpublic WebSecurityConfig(MyAuthenticationSuccessHandler myAuthenticationSuccessHandler) {this.myAuthenticationSuccessHandler = myAuthenticationSuccessHandler;}@Overrideprotected void configure(HttpSecurity http) throws Exception {// 关闭 CSRF 跨域检查(适用于前端静态页面,生产建议打开)http.csrf().disable().authorizeRequests()// 路径权限控制.antMatchers("/fun/**").hasAuthority("fun").antMatchers("/fun2/**").hasAuthority("fun2").antMatchers("/common/**", "/html/homePage.html", "/css/**", "/js/**", "/img/**").permitAll().anyRequest().authenticated().and()// 配置登录逻辑.formLogin().loginPage("/html/homePage.html") // 指定登录页面(你自己的 HTML).loginProcessingUrl("/login")     // Spring Security 登录接口(表单 action 的地址)//.defaultSuccessUrl("/html/index.html") // 登录成功跳转.failureUrl("/html/failure.html")      // 登录失败跳转.successHandler(myAuthenticationSuccessHandler).and()// 配置退出逻辑.logout().logoutUrl("/logout")                    // 默认就是 /logout,可以省略.logoutSuccessUrl("/html/homePage.html") // 退出成功跳转页面(你的登录页).invalidateHttpSession(true)             // 注销 session.deleteCookies("JSESSIONID")             // 删除 cookie,确保彻底退出.and()// 权限不足跳转页面.exceptionHandling().accessDeniedPage("/html/accessDenied.html");}
}

隐藏登录页面

需求

        用户登录后仍然有权限继续访问登录页面,这不符合操作习惯,需要改成:已登录继续访问登录页,则跳转到指定页面(一般都是跳转到登录成功后跳转的页面)。登录页面只有匿名用户可以访问

分析

        在前后端分离的项目中,后端是无法感知到用户访问了哪些页面路径,页面由前端路由。访问页面不需要经过http请求,而是可以直接在浏览器中切换视图。而在前后不分离的项目中,后端是可以感知到具体路径的。

        从以上分析中可以得出结论:在前后分离的项目中,后端项目不能控制用户访问登录页面,也不能禁止用户访问登录接口,只能决定用户访问登录接口后做出一系列反应:如果用户已登录则返回用户已登录的相关信息,未登录的用户则走项目的认证逻辑。

        因此,针对这个需求(前后分离的项目中),后端必须做逻辑控制,前端推荐做逻辑控制

实现方法

        因此如果项目前后不分离则使用anonymous()方法实现需求,反之通过过滤器配置只有匿名用户可以访问登录接口。前后分离项目中,我们可以自定义一个过滤器,该过滤器要先于UsernamePasswordAuthenticationFilter执行,这样才能避免已登录用户再次走认证逻辑。我们也可以继承UsernamePasswordAuthenticationFilter,在认证前判断是否是匿名用户,如果是匿名用户则走认证逻辑,否则返回用户已登录的相关信息。

备注

        因为篇幅原因,这里只做前后不分离的实现案例。

第一步:在WebSecurityConfig中加如如下配置:

                // 指定只有未登录用户可以访问登录页.antMatchers("/html/homePage.html").anonymous()

第二步:启动项目并测试(略)。

http://www.lryc.cn/news/606419.html

相关文章:

  • AUTOSAR进阶图解==>AUTOSAR_SRS_FreeRunningTimer
  • 基于STM32设计的景区便民服务系统(NBIOT)_261
  • 04百融云策略引擎项目laravel实战步完整安装composer及tcpdf依赖库和验证-优雅草卓伊凡
  • Docker 实战 -- cloudbeaver
  • C++手撕简单KNN
  • Apache Tomcat样例目录session操纵漏洞解读
  • vue+elementUI上传图片至七牛云组件封装及循环使用
  • python逻辑回归:数学原理到实战应用
  • 电子电气架构 --- 车载48V系统开辟全新道路
  • YOLO+Pyqt一键打包成exe(可视化,以v5为例)
  • 在Trae中使用MoonBit月兔1 创建项目
  • 极客大挑战2020(部分wp)
  • 材质:3D渲染的隐形支柱
  • window怎么安装pyqt6以及 安装 pythonqt6 会遇到的问题和怎么解决
  • Ubuntu 下配置 NVIDIA 驱动与 CUDA 环境(适配 RTX 4060Ti)
  • Leetcode-206.反转链表
  • 【前端知识】JS单线程模型深入解析
  • LangGraph认知篇-Send机制
  • 掌握Python三大语句:顺序、条件与循环
  • 【生活系列】MBTI探索 16 种性格类型
  • springcloud04——网关gateway、熔断器 sentinel
  • 难以逾越的夏天
  • 【2025/07/31】GitHub 今日热门项目
  • Excel 知识点汇总
  • JavaScript内存管理完全指南:从入门到精通
  • ABS系统专用磁阻式汽车轮速传感器
  • ansible简单playbook剧本例子
  • RHEL 8.10 离线安装 Ansible 完整教程
  • 30天入门Python(基础篇)——第25天:标准库学习之OS模块
  • 设计模式:责任链模式 Chain of Responsibility