前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Security认证和授权(二)

Spring Security认证和授权(二)

作者头像
Java技术债务
发布2022-08-09 13:43:41
5310
发布2022-08-09 13:43:41
举报
文章被收录于专栏:Java技术债务

目录

1. 默认数据库认证和授权

1.1 资源准备

首先准备三个不同权限的接口

代码语言:javascript
复制
@GetMapping("/admin/test")
@ResponseBody
public String adminTest() {
    return "admin test";
}

@GetMapping("/user/test")
@ResponseBody
public String userTest() {
    return "user test";
}

@GetMapping("/web/test")
@ResponseBody
public String webTest() {
    return "web test";
}

假设在/admin/test/下的内容是系统后台管理相关的 API,在/web/test下的内容是面向客户端公开访 问的API,在/user/test/下的内容是用户操作自身数据相关的API;显然,/admin/test必须拥有管理员权限才能进行操作,而/user/test必须在用户登录后才能进行操作。

1.2 资源授权的配置

代码语言:javascript
复制
/**
 * @author: Java技术债务
 * @Date: 2021/6/5 18:45
 * Describe: SpringSecurity配置
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/test/**").hasRole("admin")
                .antMatchers("/user/test/**").hasRole("user")
                .antMatchers("/web/test/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();

    }
}

antMatchers()是一个采用ANT模式的URL匹配器。ANT模式使用?匹配任意单个字符,使用 匹配0或任意数量的字符,使用*匹配0或者更多的目录。antMatchers(“/admin/test/“)相当于匹配 了/admin/test/下的所有API。此处我们指定当其必须为admin角色时才能访问,/user/test/与之同 理。/web/test/下的API会调用permitAll()公开其权限。

重启服务测试

页面显示403错误,表示该用户授权失败(401代表该用户认证失败)。也就是说,本次访问已经通过了认证环节,只是在授权的时候被驳回了。认证环节是没有问题的,因为Spring Security默认的用 户角色正是user。 HTTP状态码(HTTP Status Code)是由RFC 2616定义的一种用来表示一个HTTP请求响应状态的 规范,由3位数字组成。通常用2XX表示本次操作成功,用4XX表示是客户端导致的失败,用5XX表示 是服务器引起的错误。

访问/web/test/却成功了,因为/web/test/是允许所有角色访问。

1.3 基于内存的多用户支持

到目前为止,我们仍然只有一个可登录的用户,怎样引入多用户呢?非常简单,我们只需实现一 个自定义的UserDetails Service即可。

代码语言:javascript
复制
package com.demo.springSecurityFirstDemo.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * @author: Java技术债务
 * @Date: 2021/6/5 18:45
 * Describe: SpringSecurity配置
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/test/**").hasRole("ADMIN")
                .antMatchers("/user/test/**").hasRole("USER")
                .antMatchers("/web/test/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();

    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(User.withUsername("user").password("1").roles("USER").build());
        userDetailsManager.createUser(User.withUsername("admin").password("1").roles("USER", "ADMIN").build());

        return userDetailsManager;
    }
}

为其添加一个@bean注解,便可被 Spring Security 发现并使用。Spring Security支持各种来源的用户数据,包括内存、数据库、LDAP等。它们被抽象为一个UserDetailsService接口,任何实现了 UserDetailsService 接口的对象都可以作为认证数据源。在这种设计模式下,Spring Security 显得尤为灵活。 IMemory UserDetailsManager是 UserDetails Service接口中的一个实现类,它将用户数据源寄存在内存里,在一些不需要引入数据库这种重数据源的系统中很有帮助。

1.4 认证和授权

除了IMemoryUserDetailsManagsr, Spring Security还提供另一个UserDetailsService实现类: JdbcUserDetailsManager。

JdbcUserDetailsManager帮助我们以JDBC的方式对接数据库和Spring Security,它设定了一个默认的数据库模型,只要遵从这个模型,在简便性上,JdbcUserDetailsManager甚至可以媲美InMemory UserDetailsManager。

1.4.1 数据库准备

数据库方面的不需要我多说什么了吧。

唯一要说的就是创建的表

JdbcUserDetailsManager需要两个表,其中users表用来存放用户名、密码和是否可用三个信息, authorities表用来存放用户名及其权限的对应关系。将其复制到MySQL命令窗口执行时,会报错,因为该语句是用hsqldb创建的,而MySQL不支持 varchar_ignorecase这种类型。怎么办呢?很简单,将varchar_ignorecase改为MySQL支持的varchar即可。

1.4.2 编码

JdbcUserDetailsManager与InMemoryUserDetailsManager在用法上没有太大区别,只是多了设置 DataSource 的环节。Spring Security 通过 DataSource 执行设定好的命令。

例如,此处的createUser函数 实际上就是执行了下面的SQL语句。

查看 JdbcUserDetailsManager 的源代码可以看到更多定义好的 SQL 语句,诸如deleteUserSql、 updateUserSql等,这些都是JdbcUserDetailsManager与数据库实际交互的形式。当然, JdbcUserDetailsManager 也允许我们在特殊情况下自定义这些 SQL语句,如有必要,调用对应的setXxxSql方法即可。

当使用Spring Security默认数据库模型应对各种用户系统时,难免灵活性欠佳。尤其是在对现有的 系统做Spring Security嵌入时,原本的用户数据已经固定,为了适配Spring Security而在数据库层面进行 修改显然得不偿失。强大而灵活的Spring Security对这方面进行了改进。

2. 自定义数据库模型的认证与授权

2.1 实现UserDetails

之前使用了InMemoryUserDetailsManager 和 JdbcUserDetailsManager 两个UserDetailsService 实现类。生效方式也很简单,只需加入 Spring 的 IoC 容器,就会被 Spring Security自动发现并使用。自定义数据库结构实际上也仅需实现一个自定义的UserDetails Service。

UserDetailsService仅定义了一个loadUserBy Username方法,用于获取一个UserDetails对象。UserDetails对象包含了一系列在验证时会用到的信息,包括用户名、密码、权限以及其他信息,Spring Security会根据这些信息判定验证是否成功。

也就是说,不管数据库结构如何变化,只要能构造一个UserDetails即可,下面就来实现这个过 程。

2.2 数据库以及表准备

SecurityUser.java

代码语言:javascript
复制
package com.demo.springSecurityFirstDemo.security;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

/**
 * @Author Java技术债务
 * @Date 2022-03-26 19:12:56
 * @Desc *
 */
public class SecurityUser implements UserDetails {
    private Integer id;

    private String username;

    private String password;

    private boolean enable;

    private List<GrantedAuthority> authorityList;

    public Integer getId() {
        return id;
    }

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

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

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

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public List<GrantedAuthority> getAuthorityList() {
        return authorityList;
    }

    public void setAuthorityList(List<GrantedAuthority> authorityList) {
        this.authorityList = authorityList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorityList;
    }

    @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 enable;
    }
}

实现UserDetails定义的几个方法: ◎ isAccountNonExpired、isAccountNonLocked 和 isCredentialsNonExpired 暂且用不到,统一返回 true,否则Spring Security会认为账号异常。 ◎ isEnabled对应enable字段,将其代入即可。 ◎ getAuthorities方法本身对应的是roles字段,但由于结构不一致,所以此处新建一个,并在后续 进行填充。

2.3 实现UserDetailsService

代码语言:javascript
复制
package com.demo.springSecurityFirstDemo.security;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.springSecurityFirstDemo.model.UserModel;
import com.demo.springSecurityFirstDemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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

/**
 * @Author Java技术债务
 * @Date 2022-03-22 22:19:51
 * @Desc *
 *
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;
    /**
     * Security的登录,User赋予权限
     *
     * @param userName
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        System.out.println(userName);
        QueryWrapper<UserModel> qw = new QueryWrapper<>();
        qw.eq("user_name", userName);
        UserModel userModel = userService.getOne(qw);
        if (userModel == null) {
            throw new UsernameNotFoundException("user not exist!");
        }
        SecurityUser securityUser = new SecurityUser();
        securityUser.setId(userModel.getId());
        securityUser.setUsername(userModel.getUserName());
        securityUser.setPassword(userModel.getPassword());
        securityUser.setAuthorityList(AuthorityUtils.commaSeparatedStringToAuthorityList(userModel.getRoles()));
        securityUser.setEnable(userModel.getIsDeleted() == 0);

        return securityUser;
    }
}

其中AuthorityUtils.commaSeparatedStringToAuthorityList(userModel.getRoles()) 是将逗号拼接的权限字符串,比如:ROLE_USER,ROLE_ADMIN。

SimpleGrantedAuthority是GrantedAuthority的一个实现类。Spring Security的权限几乎是用 SimpleGrantedAuthority生成的,只要注意每种角色对应一个GrantedAuthority即可。另外,一定要在自 己的UserDetailsService实现类上加入@Service注解,以便被Spring Security自动发现。

[WebSecurityConfig.java](http://WebSecurityConfig.java) 完整代码

代码语言:javascript
复制
package com.demo.springSecurityFirstDemo.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.JdbcUserDetailsManager;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * @author: Java技术债务
 * @Date: 2021/6/5 18:45
 * Describe: SpringSecurity配置
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/test/**").hasRole("ADMIN")
                .antMatchers("/user/test/**").hasRole("USER")
                .antMatchers("/web/test/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();

    }
}

2.4 启动程序测试

登录数据库保存的账号密码用户名:user密码:1权限role_user

返回配置的成功页面

然后依次访问不同权限的接口

  • 访问/admin/test
  • 访问/user/test

登录数据库保存的账号密码用户名:admin密码:1权限为role_admin和role_user

访问/admin/test和/user/test/正常。

如果登录不存在的用户测试结果如图。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 默认数据库认证和授权
    • 1.1 资源准备
      • 1.2 资源授权的配置
        • 1.3 基于内存的多用户支持
          • 1.4 认证和授权
            • 1.4.1 数据库准备
            • 1.4.2 编码
        • 2. 自定义数据库模型的认证与授权
          • 2.1 实现UserDetails
            • 2.2 数据库以及表准备
              • 2.3 实现UserDetailsService
                • 2.4 启动程序测试
                相关产品与服务
                数据库
                云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档