springboot 简单集成 spring security(一)

1、spring security 简介

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。(转自百度百科)

2、spring security 作用

spring security在小编看来主要有两个作用,用户的认证和授权。用户的认证我们能够很好的理解,当用户想要进入我们的系统中进行某种操作时,就需要进行登陆操作,如果当前用户没有登陆就需要重定向到登陆界面,只有用户登陆后才能够进行某种操作。授权是当用户登陆后我们系统中有的功能是不能够让用户操作的,这时我们就需要给用户授权,当用户想要操作某种的功能时需要有相应的权限。

3、引入

3.1 pom.xml
<!--引入spring security依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2 测试

只要spring boot引入spring security无需进行任何配置就能够使用,这里我们用一个小例子展示一下。

3.2.1 controller
@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("/selByPhone")
    public Object selByPhone(){
        return "test";
    }
}

这里我们在项目中引入依赖后直接创建一个controller类。

3.2.2 启动

启动项目后会发现在控制台中显示一串代码,这里我们通过信息能够发现这时一个密码。
请添加图片描述

这里我们先不管这个密码,在浏览器中直接访问接口。
请添加图片描述
当我们访问接口后会发现我们跳转到了一个登陆页面

请添加图片描述

这里我们跳转的这个登陆页面是spring security提供的登陆页面,说明我们访问的这个接口需要登陆后才能够访问。这里我们输入spring security默认的用户名和密码(这里我们进行任何配置)。
在这里插入图片描述

用户名:user
密 码:在我们启动时生成的那串密码就是密码(每次启动生成的密码都不一样)
这时我们就能够访问到我们的接口了
在这里插入图片描述

显然这样的方式只是一个简单的测试,在实际的应用中肯定不是这样的,我们希望能够自己设置账户名和密码,这里我们有两种方式,第一种方式是在项目中进行配置,另一种是再数据库中查询验证。

3.3 项目中配置用户名和密码

3.3.1 配置文件中配置(application.properties)
#用户名
spring.security.user.name=zzm
#密码
spring.security.user.password=123456
#角色
spring.security.user.roles=admin
启动项目

当我们启动项目后就能够看到,控制台中并没有生成随机的账号和密码,这里说明我们在配置文件中配置的用户名和密码生效了。这里我们再次访问接口。
这里我们在登陆页面输入我们配置的用户名和密码
在这里插入图片描述

访问成功
在这里插入图片描述

当然这种方式也不是我们想要的

3.3.2 内存中配置

在内存中配置需要我们新建一个spring security配置类,这个配置类要继承WebSecurityConfigurerAdapter,在这里我们重写configure(AuthenticationManagerBuilder auth)这个方法这样我们才能够在内存中配置账户和密码。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        //提供加密方式
        return new BCryptPasswordEncoder(); //在这里一定要制定加密方式,否则在项目启动时不会报错但是在登陆是会提示密码错误
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(passwordEncoder()) // 指定加密方式
                .withUser("zzm").password(passwordEncoder().encode("123456")).roles("admin")
                .and()
                .withUser("zzm1").password(passwordEncoder().encode("123456")).roles("USER");
    }

}

启动项目

如果我们在内存中配置了用户名和密码,那么我们之前在配置文件中配置的用户名和密码就会失效。
在这里插入图片描述

在配置文件中配置的账户和密码失效
在这里插入图片描述
登陆成功

当然以上方法在实际的项目中不会这么应用,我们一般都是在数据库中读取用户名和密码

3.4 数据库读取用户名和密码

3.4.1 项目结构

在这里插入图片描述

3.4.2数据库信息

在这里插入图片描述

主要由用户表、角色表、用户和角色配置表组成

3.4.3 entity

role

public class Role {
    private int id;

    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

user

public class User implements UserDetails {
    private Integer id;

    private String userId;

    private String userName;

    private String phone;

    private String password;

    private String salt;

    private List<Role> roleList;

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    //将用户的角色信息添加到security中
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        roleList.forEach(v -> {
            grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+v.getName()));
        });
        return grantedAuthorities;
    }

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

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

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public User() {
    }

    public User(Integer id, String userId, String userName, String phone, String password, String salt) {
        this.id = id;
        this.userId = userId;
        this.userName = userName;
        this.phone = phone;
        this.password = password;
        this.salt = salt;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", phone='" + phone + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                '}';
    }
}
3.4.4 mapper

这里我们应用mybatis操作数据库(pom文件和配置文件这里就不贴出来了)
RoleMapper

public interface RoleMapper {

    /**
     * 查询用户角色
     * @param id
     * @return
     */
    @Select({
            "SELECT b.id,b.role" ,
                    "FROM user_role a,role b" ,
                    "WHERE a.role_id = b.id and a.user_id = #{id}"
    })
    List<Role> findRolesByUserId(Integer id);
}

UserMapper

public interface UserMapper {

    /**
     * 查询用户信息
     * @param phone
     * @return
     */
    @Select({
            "SELECT" ,
                    "user_id userId,user_name userName,password,salt" ,
                    "FROM `user`" ,
                    "WHERE phone = #{phone}"
    })
    User selUserByPhone(String phone);
}

3.4.5 service

在service层我们需要实现查询用户信息和角色信息,然后将系信息返回到security中。这里需要实现一个 UserDetailsService 接口。为了符合项目的接口我们将我们自己创建的service集成UserDetailsService,然后再impl类中实现UserService。然后重写loadUserByUsername()方法
UserService

public interface UserService extends UserDetailsService {
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //  查询用户信息
        User user = userMapper.selUserByPhone(s);
        if(user != null){
            // 查询角色信息
            List<Role> role = roleMapper.findRolesByUserId(user.getId());
            // 设置用户角色信息(在实体类中)
            user.setRoleList(role);
            System.out.println(user.toString());
        }else {
            //不存在用户返回一个空的实体类,然后security会返回错误信息
            return new User();
        }
        return user;
    }
}
3.4.6 SecurityConfig

在配置类中我们要设置验证方式要应用我们自己重写的loadUserByUsername方法。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Bean
    public PasswordEncoder passwordEncoder(){
        //提供加密方式
        return new BCryptPasswordEncoder();
    }

    //配置登陆验证方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //指定登陆用我们自己的方法
        auth.userDetailsService(userService)
                .passwordEncoder(passwordEncoder());
    }


}
3.4.7 测试

在这里插入图片描述
在这里插入图片描述

4、结束语

这里只是简单的应用spring security,还有许多的配置等我们想要的方式都没有展示出来,其他的方式会在下一篇中进行详细的讲解。


版权声明:本文为aimeng555原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。