springboot security登录认证

简介

目标

  • springBoot集成spring security 实现用户登录认证。
  • 自定义用户名密码等用户信息。
  • 添加根据用户角色授权。
  • 自定义登录页。

spring security

Spring Security 是 Spring 社区的一个顶级项目,也是 Spring Boot 官方推荐使用的安全框架。

基础功能

新建项目

  • 开发工具:idea
  • Create New Project -> Spring Initializr ->next-> 添加项目信息
  • 添加依赖 Web -> spring web,Security -> Spring Security。

pom.xml自动生成依赖:

    <dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

controller

新建controller目录在这个目录下新建控制类LoginController

package indi.xzw.spring_security.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {
    @RequestMapping("/")
    public String index(){
        return "login success";
    }
}

修改配置文件

设置登录的用户名和密码

server:
  port: 8080
spring:
  security:
    user:
      name: xzw
      password: 123456

运行

访问http://127.0.0.1:8080/

114

输入用户名和密码后返回login success

自定义用户信息

  • 我们不想用户名和密码写在配置文件中,这个时候可以继承WebSecurityConfigurerAdapter来自定义配置

  • 用户信息存储方式有三种

    • 基于内存的用户存储:通过inMemoryAuthentication()方法,我们可以启用、配置并任意填充基于内存的用户存储。
    • 基于数据库表进行认证:使用jdbcAuthentication()方法,通过JDBC进行访问用户信息。
    • **基于LDAP进行认证:**使用ldapAuthentication()方法
    • 自定义认证:使用UserDetailsService() 方法

这里我们演示自定义认证。

UserDetailsService

新建目录service,在该目录下新建类SecurityUserDetailService实现接口UserDetailsService。

package indi.xzw.spring_security.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.ArrayList;
import java.util.List;

public class SecurityUserDetailService implements UserDetailsService {
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        UserDetails userDetails =null;
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        authList.add(new SimpleGrantedAuthority("ROLE_USER"));
        authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        String password ="654321";
        //如果使用BCryptPasswordEncoder加密方式就去掉这两行注释
        //BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        //password = encoder.encode("654321");
        userDetails = new User(username,password,true,true,true,true,authList);
        return userDetails;

    }
}

密码加密

spring securit要求密码加密否则会报错,我们可以使用security提供的BCryptPasswordEncoder,也自定义密码加密加密,只要实现接口PasswordEncoder。

新建entity目录在这个目录下新建MyPasswordEncoder类,这个类并没有真正的加密还是返回原来的字符串,只是为了不报错。

package indi.xzw.spring_security.entity;

import org.springframework.security.crypto.password.PasswordEncoder;

public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(charSequence.toString());
    }
}

security配置

新建config目录在该目录下新建SecurityConfig类继承WebSecurityConfigurerAdapter。

package indi.xzw.spring_security.config;

import indi.xzw.spring_security.entity.MyPasswordEncoder;
import indi.xzw.spring_security.service.SecurityUserDetailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /* 新版本spring security必须要有密码加密 */
    @Bean
    PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder();
        return new MyPasswordEncoder();
    }

    @Override
    public UserDetailsService userDetailsService() {
        return new SecurityUserDetailService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService());
    }
}
  • passwordEncoder:返回加密类,这里注释掉的是BCryptPasswordEncoder加密类,如果使用这个加密类需要在SecurityUserDetailService,把密码加密。MyPasswordEncoder是我们自己写的假的加密类,密码直接明文就行。
  • 重写userDetailsService和configure接口

运行

这个时候访问访问http://127.0.0.1:8080/ 密码就不是application.yaml中的了,而是我们在代码中设计的密码。

授权

修改controller

添加/user和/admin

@RestController
public class LoginController {
    @RequestMapping("/")
    public String index(){
        return "login success";
    }
    @RequestMapping("/user")
    public String get(){
        return "user:xzw";
    }

    @RequestMapping("/admin")
    public String getAdmin(){
        return "admin:xzw";
    }
}

修改SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /* 新版本spring security必须要有密码加密 */
    @Bean
    PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder();
        return new MyPasswordEncoder();
    }

    @Override
    public UserDetailsService userDetailsService() {
        return new SecurityUserDetailService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http)throws Exception{
        http.authorizeRequests()
                /**
                 * ANT模式使用-匹配任意单个字符,使用*表示:匹配0或者任意数量的字符,
                 * 使用**匹配0或者更多的目录
                 */
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAnyRole("ADMIN","USER")
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }
}
  • 添加了路由角色,访问/admin需要admin权限,访问/user需要admin和user都行
  • 角色在UserDetailsService中用authList.add赋予

运行

我们注释掉UserDetailsService中admin角色,访问http://127.0.0.1:8080/admin发现没有权限访问http://127.0.0.1:8080/user可以访问,如果恢复admin角色则都能访问。

自定义登录页

 等录页放在static和templates区别:
     1.如果我们loginPage里面写的是controller的uri,那么我们就应该在对应的uri那里返回一个页面
     同时我们需要导入模板引擎的依赖,否则识别不了这个页面,会报404。同时这是login页面位于templates下的做法。      
     2.如果我们的login页面放在static下,那么我们只需要在loginPage下写上login.html的位置即可,如“/login.html”

开发登录页面

在resources/static下新建myLogin.html

注意:这里表单的action为 /login 提交方式为POST

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h2>自定义登录页面</h2>
<hr>
<form action="/login" method="POST">
    用户名<input type="text" name="username"/> <br>
    密码 <input type="password" name="password"> <br>
    <input type="submit" value="登录">
</form>
</body>
</html>

修改SecurityConfig

package indi.xzw.spring_security.config;

import indi.xzw.spring_security.entity.MyPasswordEncoder;
import indi.xzw.spring_security.service.SecurityUserDetailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /* 新版本spring security必须要有密码加密 */
    @Bean
    PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder();
        return new MyPasswordEncoder();
    }

    @Override
    public UserDetailsService userDetailsService() {
        return new SecurityUserDetailService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http)throws Exception{
        http.csrf().disable() // 关闭 CSRF 保护功能,否则不支持 Post 请求
                .authorizeRequests()
                /**
                 * ANT模式使用-匹配任意单个字符,使用*表示:匹配0或者任意数量的字符,
                 * 使用**匹配0或者更多的目录
                 */
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAnyRole("ADMIN","USER")
                .antMatchers("/myLogin.html").permitAll() // myLogin.html 页面无需登录即可访问
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/myLogin.html") // 每当需要登录时浏览器跳转到 myLogin.html 页面
                .loginProcessingUrl("/login") // 自定义登录提交地址,默认地址是 /login
                .and().httpBasic(); // 定义如何验证用户,此项代表弹出浏览器认证窗口
    }
}

运行

115

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×