• spring security 身份认证的实现


    目录

    一、引入依赖,开箱即用

    二、自定义用户名和密码

    三、自定义登陆页面


    一、引入依赖,开箱即用

    1、基于Spring Boot实现,首先引入依赖,版本跟随spring boot

    1. <!-- 实现对 Spring MVC 的自动化配置 -->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-web</artifactId>
    5. </dependency>
    6. <!-- 实现对 Spring Security 的自动化配置 -->
    7. <dependency>
    8. <groupId>org.springframework.boot</groupId>
    9. <artifactId>spring-boot-starter-security</artifactId>
    10. </dependency>

    2、编写 controller

    1. import org.springframework.web.bind.annotation.GetMapping;
    2. import org.springframework.web.bind.annotation.RequestMapping;
    3. import org.springframework.web.bind.annotation.RestController;
    4. /**
    5. * @author Administrator
    6. * @date 2019/7/6/006 20:25
    7. * @Version 1.0
    8. */
    9. @RestController
    10. @RequestMapping("/admin")
    11. public class AdminController {
    12. @GetMapping("/test")
    13. public String test(){
    14. return "Spring Security Test";
    15. }
    16. }

    引入Spring Security之后 ,访问 API 接口时,需要首先进行登录,才能进行访问。

    测试 http://localhost:8080/admin/test,会跳转到

    该登陆页面通过以下接口生成:DefaultLoginPageGeneratingFilter#generateLoginPageHtml

    默认用户名:user,密码可以查看控制台日志获取,这个默认用户生成的逻辑在SecurityProperties.class 中,用户名和密码也可以通过属性配置进行覆盖。

    默认用户原理:

    默认情况下,UserDetailsServiceAutoConfiguration.class 自动化配置类会创建一个内存级别的

    InMemoryUserDetailsManager对象,提供认证的用户信息。

    • 添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会基于配置的信息在内存中创建一个用户User。
    • 未添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会自动在内存中创建一个用户名为 user,密码为 UUID 随机的用户 User。

    UserDetailsServiceAutoConfiguration#inMemoryUserDetailsManager

    1. @Lazy
    2. public InMemoryUserDetailsManager inMemoryUserDetailsManager(
    3. SecurityProperties properties,
    4. ObjectProvider<PasswordEncoder> passwordEncoder) {
    5. SecurityProperties.User user = properties.getUser();
    6. List<String> roles = user.getRoles();
    7. return new InMemoryUserDetailsManager(User.withUsername(user.getName())
    8. .password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
    9. .roles(StringUtils.toStringArray(roles)).build());
    10. }

    二、自定义用户名和密码

    1、方式一:基于UserDetailsService接口

    1. import org.springframework.security.core.authority.AuthorityUtils;
    2. import org.springframework.security.core.userdetails.User;
    3. import org.springframework.security.core.userdetails.UserDetails;
    4. import org.springframework.security.core.userdetails.UserDetailsService;
    5. import org.springframework.security.core.userdetails.UsernameNotFoundException;
    6. import org.springframework.stereotype.Service;
    7. @Service
    8. public class MyUserDetailsService implements UserDetailsService {
    9. // 用户信息
    10. @Override
    11. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    12. // 方式:1-内存、2-DB
    13. // 需要处理密码编码,无编码{noop} 、 User(用户名,密码,权限)
    14. UserDetails userDetails = new User("admin","{noop}123456",
    15. AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
    16. return userDetails;
    17. }
    18. }

    注意

    1. UserDetails userDetails = new User("admin","{noop}123456",
    2. AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));

    Spring security5 中新增加了加密方式,并把原有的spring security的密码存储格式改了,修改后的密码存储格式为:{id}encodedPassword

    如果密码不指定加密方式{id}会抛异常:

    支持的加密方式可以通过 PasswordEncoderFactories.class 查看

    也可以通过增加PasswordEncoder配置指定加密方式,spring security官方推荐的加密方式 BCrypt

    1. import org.springframework.context.annotation.Bean;
    2. import org.springframework.context.annotation.Configuration;
    3. org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    4. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    5. import org.springframework.security.crypto.password.PasswordEncoder;
    6. @Configuration // 标记为注解类
    7. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    8. @Bean
    9. public PasswordEncoder passwordEncoder() {
    10. return new BCryptPasswordEncoder();
    11. }
    12. }
    1. @Service
    2. public class MyUserDetailsService implements UserDetailsService {
    3. @Autowired
    4. PasswordEncoder passwordEncoder;
    5. // 用户信息
    6. @Override
    7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    8. // 方式:1-内存、2-DB
    9. // 需要处理密码编码,无编码{noop} 、 User(用户名,密码,权限)
    10. // UserDetails userDetails = new User("admin","{noop}123456",
    11. // AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
    12. UserDetails userDetails = new User("admin",passwordEncoder.encode("123456"),
    13. AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
    14. return userDetails;
    15. }
    16. }

    2、方式二:基于配置类 WebSecurityConfigurerAdapter

    创建 WebSecurityConfig配置类,继承 WebSecurityConfigurerAdapter抽象类,实现 Spring Security 在 Web 场景下的自定义配置。

    注:springboot 项目中如果引入的是 spring-boot-starter-security 依赖可以不需要添加
    @EnableWebSecurity

    因为在 spring-boot-autoconfigure-2.3.5.RELEASE.jar!/META-INF/spring.factories 下SecurityAutoConfiguration.class 中已经有引入

    但如果引入的是 spring-security-config 和 spring-security-web 依赖,则需要添加@EnableWebSecurity注解。 // 非springboot项目

    (1)重写 #configure(AuthenticationManagerBuilder auth) 方法,实现 AuthenticationManager认证管理器。

    1. import org.springframework.context.annotation.Bean;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    4. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    5. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    6. import org.springframework.security.crypto.password.PasswordEncoder;
    7. @Configuration // 标记为注解类
    8. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    9. @Bean
    10. public PasswordEncoder passwordEncoder() {
    11. return new BCryptPasswordEncoder(); // 加密方式
    12. }
    13. @Override
    14. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    15. String password = passwordEncoder().encode("123456");
    16. auth.inMemoryAuthentication() // 使用基于内存的InMemoryUserDetailsManager
    17. .passwordEncoder(passwordEncoder()) //使用 PasswordEncoder 密码编码器
    18. .withUser("admin").password(password).roles("admin") // 配置用户
    19. .and().withUser("admin2").password(password).roles("user");// 配置其他用户
    20. }
    21. }

    (2)也可以重写WebSecurityConfigurerAdapter#userDetailsService()方法或者
    WebSecurityConfigurerAdapter#userDetailsServiceBean(),并通过@Bean交给spring管理

    1. @Configuration // 标记为注解类
    2. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    3. @Bean
    4. public PasswordEncoder passwordEncoder() {
    5. return new BCryptPasswordEncoder();
    6. }
    7. // @Override
    8. // protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    9. // String password = passwordEncoder().encode("123456");
    10. // auth.inMemoryAuthentication() // 使用基于内存的InMemoryUserDetailsManager
    11. // .passwordEncoder(passwordEncoder()) //使用 PasswordEncoder 密码编码器
    12. // .withUser("admin").password(password).roles("admin") // 配置用户
    13. // .and().withUser("admin2").password(password).roles("user");// 配置其他用户
    14. // }
    15. @Bean
    16. @Override
    17. protected UserDetailsService userDetailsService() {
    18. return username -> {
    19. String pw = passwordEncoder().encode("123456");
    20. return new User("admin", pw,
    21. AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user"));
    22. };
    23. }
    24. }

    3、方式三:从数据库获取用户名和授权信息

    首先创建数据库表,用户表、角色表,权限表,用户角色关联表,角色权限关联表

    1. -- ----------------------------
    2. -- Table structure for tb_user 用户表
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `tb_user`;
    5. CREATE TABLE `tb_user` (
    6. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    7. `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
    8. `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码,加密存储',
    9. `phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '注册手机号',
    10. `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '注册邮箱',
    11. `created` datetime NOT NULL,
    12. `updated` datetime NOT NULL,
    13. PRIMARY KEY (`id`) USING BTREE,
    14. UNIQUE INDEX `username`(`username`) USING BTREE,
    15. UNIQUE INDEX `phone`(`phone`) USING BTREE,
    16. UNIQUE INDEX `email`(`email`) USING BTREE
    17. ) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
    18. -- ----------------------------
    19. -- Records of tb_user 用户数据
    20. -- ----------------------------
    21. INSERT INTO `tb_user` VALUES (37, 'admin', '$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi', '158xxx xxxx', 'xxxxxxx@gmail.com', '2019-04-04 23:21:27', '2019-04-04 23:21:29');
    22. -- ----------------------------
    23. -- Table structure for tb_role 角色表
    24. -- ----------------------------
    25. DROP TABLE IF EXISTS `tb_role`;
    26. CREATE TABLE `tb_role` (
    27. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    28. `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父角色',
    29. `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
    30. `en_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色英文名称',
    31. `description` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
    32. `created` datetime NOT NULL,
    33. `updated` datetime NOT NULL,
    34. PRIMARY KEY (`id`) USING BTREE
    35. ) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;
    36. -- ----------------------------
    37. -- Records of tb_role 角色信息
    38. -- ----------------------------
    39. INSERT INTO `tb_role` VALUES (37, 0, '超级管理员', 'swadian', NULL, '2019-04-04 23:22:03', '2019-04-04 23:22:05');
    40. -- ----------------------------
    41. -- Table structure for tb_permission 权限表
    42. -- ----------------------------
    43. DROP TABLE IF EXISTS `tb_permission`;
    44. CREATE TABLE `tb_permission` (
    45. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    46. `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父权限',
    47. `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称',
    48. `en_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限英文名称',
    49. `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '授权路径',
    50. `description` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
    51. `created` datetime NOT NULL,
    52. `updated` datetime NOT NULL,
    53. PRIMARY KEY (`id`) USING BTREE
    54. ) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '权限表' ROW_FORMAT = Dynamic;
    55. -- ----------------------------
    56. -- Records of tb_permission 权限信息
    57. -- ----------------------------
    58. INSERT INTO `tb_permission` VALUES (37, 0, '系统管理', 'System', '/', NULL, '2019-04-04 23:22:54', '2019-04-04 23:22:56');
    59. INSERT INTO `tb_permission` VALUES (38, 37, '用户管理', 'SystemUser', '/users/', NULL, '2019-04-04 23:25:31', '2019-04-04 23:25:33');
    60. INSERT INTO `tb_permission` VALUES (39, 38, '查看用户', 'SystemUserView', '', NULL, '2019-04-04 15:30:30', '2019-04-04 15:30:43');
    61. INSERT INTO `tb_permission` VALUES (40, 38, '新增用户', 'SystemUserInsert', '', NULL, '2019-04-04 15:30:31', '2019-04-04 15:30:44');
    62. INSERT INTO `tb_permission` VALUES (41, 38, '编辑用户', 'SystemUserUpdate', '', NULL, '2019-04-04 15:30:32', '2019-04-04 15:30:45');
    63. INSERT INTO `tb_permission` VALUES (42, 38, '删除用户', 'SystemUserDelete', '', NULL, '2019-04-04 15:30:48', '2019-04-04 15:30:45');
    64. INSERT INTO `tb_permission` VALUES (44, 37, '内容管理', 'SystemContent', '/contents/', NULL, '2019-04-06 18:23:58', '2019-04-06 18:24:00');
    65. INSERT INTO `tb_permission` VALUES (45, 44, '查看内容', 'SystemContentView', '/contents/view/**', NULL, '2019-04-06 23:49:39', '2019-04-06 23:49:41');
    66. INSERT INTO `tb_permission` VALUES (46, 44, '新增内容', 'SystemContentInsert', '/contents/insert/**', NULL, '2019-04-06 23:51:00', '2019-04-06 23:51:02');
    67. INSERT INTO `tb_permission` VALUES (47, 44, '编辑内容', 'SystemContentUpdate', '/contents/update/**', NULL, '2019-04-06 23:51:04', '2019-04-06 23:51:06');
    68. INSERT INTO `tb_permission` VALUES (48, 44, '删除内容', 'SystemContentDelete', '/contents/delete/**', NULL, '2019-04-06 23:51:08', '2019-04-06 23:51:10');
    69. -- ----------------------------
    70. -- Table structure for tb_user_role 用户角色关联表
    71. -- ----------------------------
    72. DROP TABLE IF EXISTS `tb_user_role`;
    73. CREATE TABLE `tb_user_role` (
    74. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    75. `user_id` bigint(20) NOT NULL COMMENT '用户 ID',
    76. `role_id` bigint(20) NOT NULL COMMENT '角色 ID',
    77. PRIMARY KEY (`id`) USING BTREE
    78. ) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户角色表' ROW_FORMAT = Dynamic;
    79. -- ----------------------------
    80. -- Records of tb_user_role 用户角色关联信息
    81. -- ----------------------------
    82. INSERT INTO `tb_user_role` VALUES (37, 37, 37);
    83. -- ----------------------------
    84. -- Table structure for tb_role_permission 角色权限关联表
    85. -- ----------------------------
    86. DROP TABLE IF EXISTS `tb_role_permission`;
    87. CREATE TABLE `tb_role_permission` (
    88. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    89. `role_id` bigint(20) NOT NULL COMMENT '角色 ID',
    90. `permission_id` bigint(20) NOT NULL COMMENT '权限 ID',
    91. PRIMARY KEY (`id`) USING BTREE
    92. ) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色权限表' ROW_FORMAT = Dynamic;
    93. -- ----------------------------
    94. -- Records of tb_role_permission 角色权限关信息
    95. -- ----------------------------
    96. INSERT INTO `tb_role_permission` VALUES (37, 37, 37);
    97. INSERT INTO `tb_role_permission` VALUES (38, 37, 38);
    98. INSERT INTO `tb_role_permission` VALUES (39, 37, 39);
    99. INSERT INTO `tb_role_permission` VALUES (40, 37, 40);
    100. INSERT INTO `tb_role_permission` VALUES (41, 37, 41);
    101. INSERT INTO `tb_role_permission` VALUES (42, 37, 42);
    102. INSERT INTO `tb_role_permission` VALUES (43, 37, 44);
    103. INSERT INTO `tb_role_permission` VALUES (44, 37, 45);
    104. INSERT INTO `tb_role_permission` VALUES (45, 37, 46);
    105. INSERT INTO `tb_role_permission` VALUES (46, 37, 47);
    106. INSERT INTO `tb_role_permission` VALUES (47, 37, 48);

    然后引入数据库相关依赖

    1. <--mybaits plus 框架-->
    2. <dependency>
    3. <groupId>com.baomidou</groupId>
    4. <artifactId>mybatis-plus-boot-starter</artifactId>
    5. <version>3.3.2</version>
    6. </dependency>
    7. <--mysql-->
    8. <dependency>
    9. <groupId>mysql</groupId>
    10. <artifactId>mysql-connector-java</artifactId>
    11. <version>5.1.39</version>
    12. </dependency>
    13. <dependency>
    14. <groupId>org.projectlombok</groupId>
    15. <artifactId>lombok</artifactId>
    16. </dependency>

    在配置文件 application.yml 中,配置数据源

    1. spring:
    2. datasource:
    3. driver-class-name: com.mysql.jdbc.Driver
    4. url: jdbc:mysql://localhost:3306/security?useSSL=false
    5. password: root
    6. username: root

    关键步骤:修改获取用户信息的 MyUserDetailsService 逻辑,改为从数据库获取

    1. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    2. import com.swadian.security.entity.TbPermission;
    3. import com.swadian.security.entity.TbUser;
    4. import com.swadian.security.mapper.TbPermissionMapper;
    5. import com.swadian.security.mapper.TbUserMapper;
    6. import org.springframework.security.core.GrantedAuthority;
    7. import org.springframework.security.core.authority.SimpleGrantedAuthority;
    8. import org.springframework.security.core.userdetails.User;
    9. import org.springframework.security.core.userdetails.UserDetails;
    10. import org.springframework.security.core.userdetails.UserDetailsService;
    11. import org.springframework.security.core.userdetails.UsernameNotFoundException;
    12. import org.springframework.stereotype.Service;
    13. import org.springframework.util.StringUtils;
    14. import javax.annotation.Resource;
    15. import java.util.ArrayList;
    16. import java.util.List;
    17. @Service
    18. public class MyUserDetailsService implements UserDetailsService {
    19. @Resource // 用户表
    20. private TbUserMapper userMapper;
    21. @Resource // 权限表
    22. private TbPermissionMapper permissionMapper;
    23. public TbUser getByUsername(String username) {
    24. return userMapper.selectList(Wrappers.lambdaQuery(TbUser.class).eq(TbUser::getUsername, username)
    25. ).stream().findFirst().orElse(null);
    26. }
    27. @Override
    28. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    29. // 从mysql查询用户
    30. TbUser user = getByUsername(username);
    31. // 封装权限的集合
    32. List authorities = new ArrayList<>();
    33. if (user != null) {
    34. // 获取用户的权限 用户->角色->权限:注,这个三个表的id被设置为一样,所以可以直接用 userId 获取权限
    35. List permissions = permissionMapper.selectList(Wrappers.lambdaQuery(TbPermission.class)
    36. .eq(TbPermission::getId, user.getId()));
    37. permissions.forEach(permission -> {
    38. if (permission != null && !StringUtils.isEmpty(permission.getEnName())) {
    39. GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getEnName());
    40. authorities.add(grantedAuthority);
    41. }
    42. });
    43. // 封装成UserDetails的实现类
    44. return new User(user.getUsername(), user.getPassword(), authorities);
    45. } else {
    46. throw new UsernameNotFoundException("用户名不存在");
    47. }
    48. }
    49. }

    spring secutiry 的 配置如下:

    1. import com.swadian.security.service.MyUserDetailsService;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Bean;
    4. import org.springframework.context.annotation.Configuration;
    5. import org.springframework.context.annotation.Lazy;
    6. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    7. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    8. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    9. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    10. import org.springframework.security.crypto.password.PasswordEncoder;
    11. @Configuration // 标记为注解类
    12. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    13. @Autowired
    14. @Lazy // 解决循环依赖
    15. private MyUserDetailsService userService;
    16. @Bean
    17. public PasswordEncoder passwordEncoder() {
    18. return new BCryptPasswordEncoder();
    19. }
    20. @Override
    21. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    22. //设置UserDetailsService的实现类
    23. auth.userDetailsService(userService);
    24. }
    25. @Override
    26. protected void configure(HttpSecurity http) throws Exception {
    27. http.formLogin(); //表单提交
    28. http.logout()
    29. .logoutUrl("/logout")
    30. .logoutSuccessUrl("/login.html"); // 退出后跳转到登陆页面
    31. http.authorizeRequests()
    32. .antMatchers("/login.html", "/error.html", "/main.html").permitAll() // 不需要认证
    33. .anyRequest()
    34. .authenticated() // 认证拦截
    35. .and().csrf().disable(); //关闭csrf防护
    36. }
    37. }

    修改下 controller 的内容,修改为返回用户信息

    1. @RestController
    2. @RequestMapping("/admin")
    3. public class AdminController {
    4. @GetMapping("/test")
    5. public Object test() {
    6. // 返回用户信息
    7. return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    8. }
    9. }

    测试结果:

    从数据库获取用户信息成功

    三、自定义登陆页面

    默认登录页面通过DefaultLoginPageGeneratingFilter#generateLoginPageHtml生成,下边替换成自定义登陆页面:login.html

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Title</title>
    6. </head>
    7. <body>
    8. <form action="/user/login" method="post">
    9. 用户名:<input type="text" name="username"/><br/>
    10. 密码: <input type="password"name="password"/><br/>
    11. <input type="submit" value="提交"/>
    12. </form>
    13. </body>
    14. </html>

    自定义登陆页面配置

    1. import com.swadian.userdemo.service.MyUserDetailsService;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Bean;
    4. import org.springframework.context.annotation.Configuration;
    5. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    6. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    7. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    8. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    9. import org.springframework.security.crypto.password.PasswordEncoder;
    10. @Configuration // 标记为注解类
    11. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    12. @Autowired
    13. private MyUserDetailsService userService;
    14. @Bean
    15. public PasswordEncoder passwordEncoder() {
    16. return new BCryptPasswordEncoder();
    17. }
    18. @Override
    19. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    20. //设置UserDetailsService的实现类
    21. auth.userDetailsService(userService);
    22. }
    23. // 自定义登陆页面配置
    24. @Override
    25. protected void configure(HttpSecurity http) throws Exception {
    26. http.formLogin() //表单提交
    27. .loginPage("/login.html") //自定义登录页面
    28. .loginProcessingUrl("/user/login") //登录访问路径,必须和表单提交接口一样
    29. //.usernameParameter("my-username") // 自定义参数名
    30. //.passwordParameter("my-password")
    31. //.defaultSuccessUrl("/main.html") //认证成功之后跳转的路径
    32. .successForwardUrl("/login/main") //认证成功之后转发的路径,必须是Post请求
    33. .failureForwardUrl("/login/error") //认证失败之后转发的路径,必须是Post请求
    34. .and()
    35. // 认证配置
    36. .authorizeRequests() //设置哪些路径可以直接访问,不需要认证
    37. .antMatchers("/user/login", "/login.html","/error.html","/main.html").permitAll()
    38. .anyRequest()
    39. .authenticated() //需要认证
    40. .and().csrf().disable(); //关闭csrf防护
    41. }
    42. // @Bean
    43. // @Override
    44. // protected UserDetailsService userDetailsService() {
    45. // return username -> {
    46. // String pw = passwordEncoder().encode("123456");
    47. // return new User("admin", pw,
    48. // AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user"));
    49. // };
    50. // }
    51. }

    通过successForwardUrl和failureForwardUrl设置登录成功和失败后的跳转页面

    1. import org.springframework.stereotype.Controller;
    2. import org.springframework.web.bind.annotation.RequestMapping;
    3. @Controller
    4. @RequestMapping("/login")
    5. public class LoginController {
    6. @RequestMapping("/main") // 认证成功后执行方法
    7. public String main() {
    8. return "redirect:/main.html"; // 跳转页面
    9. }
    10. @RequestMapping("/error") // 认证失败后执行方法
    11. public String error() {
    12. return "redirect:/error.html";
    13. }
    14. }

    需要注意的点,代码和页面两边的路径需要保持一致

    页面路径,统一放在资源包static下,至于为什么在static,后续可以探讨下

    使用过滤器的页面重定向

    比如想实现登录成功后重定向其他页面,可以利用AuthenticationSuccessHandler接口实现自定义的认证成功控制器。

    1. import org.springframework.security.core.Authentication;
    2. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    3. import javax.servlet.ServletException;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.IOException;
    7. public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    8. // 重定向 url
    9. private String redirectUrl;
    10. public MyAuthenticationSuccessHandler(String redirectUrl) {
    11. this.redirectUrl = redirectUrl;
    12. }
    13. @Override
    14. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    15. response.sendRedirect(redirectUrl);
    16. }
    17. }

    在代码中使用 successHandler() 进行配置

    1. // 自定义登陆页面配置
    2. @Override
    3. protected void configure(HttpSecurity http) throws Exception {
    4. http.formLogin() //表单提交
    5. .loginPage("/login.html") //自定义登录页面
    6. .loginProcessingUrl("/my-user/login") //登录访问路径,必须和表单提交接口一样
    7. .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
    8. .and()
    9. .authorizeRequests() //设置哪些路径可以直接访问,不需要认证
    10. .antMatchers("/login.html","/error.html","/main.html").permitAll()
    11. .anyRequest()
    12. .authenticated() //需要认证
    13. .and().csrf().disable(); //关闭csrf防护
    14. }

    同样的,当登录认证失败后,想做一些事情,可以实现AuthenticationFailureHandler接口。

  • 相关阅读:
    浏览器工作原理与实践学习笔记(一)宏观视角下的浏览器
    一、MongoDB安装(CentOS7)
    JSP 基本介绍及使用。
    shell脚本之文件读写
    IDEA Error: java: -source 1.5中不支持 lambda 表达式和 Error:java: Compilation failed
    是的,决定放弃算法去机器学习了
    游戏设计模式专栏(十三):在Cocos游戏开发中运用责任链模式
    【云原生之Docker实战】使用docker部署Wiznote私人笔记系统
    新手开抖店之前,这三个核心点,一定要提前了解!
    软件测试高频面试题真实分享/网上银行转账是怎么测的,设计一下测试用例。
  • 原文地址:https://blog.csdn.net/swadian2008/article/details/126191823