SpringSecurity 入门配置

本文介绍了如何在SpringBoot项目中集成SpringSecurity进行初步的权限管理配置,包括启用Spring Security、配置默认登录页面、实现数据库认证以及自定义登录页面。通过示例展示了从创建项目、添加依赖、配置安全类到数据库交互的完整流程,帮助初学者理解SpringSecurity的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在实习中,需要用到Oauth协议来进行资源的认证,所以在此记录一下,让有需要的人少走一些弯路。(PS:Google真是个好东西,Github和官网也是好东西,虽然英文看起来挺费劲的)

这是官方实例:
认证服务器:https://github.com/spring-cloud-samples/authserver
SSO客户端:https://github.com/spring-cloud-samples/sso

要了解Oauth我们首先需要实现登录,这里使用SpringSecurity
刚接触Springboot和Security的人可能会比较难理解,所以我一步步来,编辑器使用的IDEA:

关于Spring Security 最好还是看官网说明
https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#pe-dpe

使用Security进行简单的密码登录验证
首先创建一个Springboot项目,引入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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

我们新建一个Config文档用来放配置类
新建SecurityConfig,使其继承WebSecurityConfigurerAdapter,并使用@EnableWebSecurity 开启Spring Security

@Configuration
@EnableWebSecurity  //开启Spring Security
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //加上这个方法就是不去管方法是否已被弃用
    @SuppressWarnings("deprecation")
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin().permitAll()
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("haha").password("haha").roles("USER");
    }
}

注意,如果你不使用别的passwordEncoder,要加上

@Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

因为密码的一般格式是:
{id}encodedPassword

这样的id是一个标识符,用于查找应使用哪个PasswordEncoder,并且encodedPassword是所选PasswordEncoder的原始编码密码。 该ID必须位于密码的开头,以{开始并以}结束。 如果找不到id,则id将为空。有不同的PasswordEncoder,如{bcrypt}和{sha256}等。
这样,就会使用默认的文本格式的passwordEncoder

否则因为没有提供明确的PasswordEncoder,将会出现下面的错误
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
这里写图片描述

在configure中配置的是具体配置,例如这里就是访问所有的接口都需要登录,你可以配置哪些不需要。也可以使用自定义的登录页面,这里我们使用Spring Security默认的登录页面即可。

然后我们增加一个接口

@RestController
public class UserController {

    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity add(){
        return ResponseEntity.ok("hello");
    }
}

访问http://localhost:8080 ,因为未登录,所以会跳转到http://localhost:8080/login
这里写图片描述

输入配置的账号密码后,跳转到输出hello的页面

当然,这种将账号密码写死的方式是不可能使用的,应使用数据库来存储这些用户信息

数据库使用MongoDB(Mysql也可以),关于JPA下的MongoDB可自行了解
在pom.xml中引入依赖

   <!--springboot下的Mongdb依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

在application.yml中添加(默认是application.properties,但更推荐yml,树状式结构让配置更易看)

spring:
  data:
    mongodb:
      database: auth
      uri: mongodb://localhost:27017

文件结构如下,采取分层设计模式
这里写图片描述

首先创建一个实现了UserDetails的model类,或继承User也可以,根据需要改写重写的方法

@Document(collection="user")
public class User implements Serializable,UserDetails {

    @Id
    String id;

    @Indexed(unique = true)
    String username;

    String password;

    @CreatedDate
    private Date createdAt;

    @LastModifiedDate
    private Date updatedAt;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

//可改为从权限表从获取权限
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

dao层只需实现MongoRepository接口即可,默认的CRUD会自动生成,需要额外的可以自己写sql语句或按其规定写方法

public interface UserRepository extends MongoRepository<User,String> {
     public User findByUsername(String username);
}

还需要实现自定义的UserDetailsService

public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user=userRepository.findByUsername(username);
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        System.out.println("haha");
        authorities.add(new SimpleGrantedAuthority("ROLE_" + "ADMIN"));
        org.springframework.security.core.userdetails.User userDetails=new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorities);
        return user;
    }
}

这里我返回的Spring Security的User,你可以继承它来实现自己的额外需求,loadUserByUsername返回UserDetails,权限需要注意要加”ROLE_”前缀,Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了

接下来在SecurityConfig中配置自定义的UserDetailsService

    // register as a bean,否则在context中@Autowired非context管理的类会报null exception
    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService();
    }

@Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
                 auth.userDetailsService(userDetailsService());
    }

这个时候,就可以成功的从数据库中获取用户信息来进行登录验证了
我们使用Test来添加用户

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class AuthApplicationTests {

    @Autowired
    private UserRepository userRepository;

    PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();

    @Test
    public void addTest(){
        User user=new User();
        user.setUsername("xixi");
        String password="xixi";
        user.setPassword(passwordEncoder.encode(password));
        userRepository.save(user);
    }
}

可以看到生成的user中密码不是已明文的形式存储的了
这里写图片描述

再在SecurityConfig中配置PasswordEncoder

@Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

@Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
                 auth.
                         userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }

因为这里我们使用了BCryptPasswordEncoder,而不是使用明文的文本形式的NoOpPasswordEncoder,所以将下面去掉

    @SuppressWarnings("deprecation")
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

再次登录,发现实现我们所要的目的了

如果前后端不分离,而你又希望使用自己的登录页面,这时只需要写一个Controller去处理GET方法的/login即可

@Controller
public class LoginLogoutController {

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

并在SecurityConfig中配置

http
                .formLogin().loginPage("/login").permitAll()

如果想让用户登录后跳转到首页,只需要在configure中配置即可

http
                .formLogin().successForwardUrl("/success").permitAll();

添加一个Controller,用来跳转到登录成功所要到的页面

@RestController
public class LoginLogoutController {

    @RequestMapping(value = "/success",method = RequestMethod.POST)
    public String success(){
        return "success";
    }

}

注意,默认接受POST方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值