Spring Security实战--(二)数据库实现

一、数据库的实现

Spring Security提供了默认数据库模板和自定义数据库两种方式,默认数据库方式就不说了,因为基本没人用到

二、自定义数据库

2.1 不同权限controller建立

建立下相关controller,为不同权限做准备

Spring Security实战--(二)数据库实现_第1张图片
代码如下,注释已经解释清楚了,就不多说了

@RestController
@RequestMapping("admin/api")
public class AdminController {

    /**
     * 管理员访问路径
     * @return
     */
    @GetMapping("hello")
    public String hello(){
        return "admin,hello";
    }

}

@RestController
@RequestMapping("/user/api")
public class UserController {

    /**
     * 注册用户访问路径
     * @return
     */
    @GetMapping("hello")
    public String hello(){
        return "user,hello";
    }

}

@RestController
@RequestMapping("/app/api")
public class AppController {

    /**
     * 游客访问路径
     * @return
     */
    @GetMapping("hello")
    public String hello(){
        return "app,hello";
    }
}

2.2 不同权限资源授权配置

顺便演示一下内存多用户配置

2.2.1 内存多用户配置

配置访问路径权限和用户组权限

package com.example.configuration;

import org.springframework.context.annotation.Bean;
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;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("{noop}123").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("{noop}123").roles("USER,ADMIN").build());
        return manager;
    }
}

在这里使用InMemoryUserDetailsManager 生成了两个用户,将用户寄存在内存中

直接登录http:localhost:8080/user/api/hello,会跳转到登录页面

Spring Security实战--(二)数据库实现_第2张图片
使用user用户登录
在这里插入图片描述
跳转到admin/api/hello
Spring Security实战--(二)数据库实现_第3张图片
显示403错误,即认证成功但是权限不够

2.2.2 数据库多用户

Security提供了UserDetailsService实现类,可以利用他实现自定义的数据库查找用户,使用loadUserByUsername来获取用户

因此这里工程需要接入MyBatis

添加相关的配置并建立mysql数据库

CREATE TABLE USERS(
	ID BIGINT(20) PRIMARY KEY NOT NULL AUTO_INCREMENT,
	USERNAME VARCHAR(50) NOT NULL,
	PASSWORD VARCHAR(60),
	ENABLE TINYINT(4) NOT NULL DEFAULT '1' COMMENT '用户是否可用',
	ROLES TEXT CHARACTER SET UTF8 COMMENT '用户角色'
);

INSERT INTO USERS(USERNAME,PASSWORD,ENABLE,ROLES) VALUES("admin","123456","1","ROLE_ADMIN,ROLE_USER");
INSERT INTO USERS(USERNAME,PASSWORD,ENABLE,ROLES) VALUES("user","123456","1","ROLE_USER");

其中,USERS表存用户、密码、是否可用和用户角色(多个角色用逗号分隔)

编写相关的User实体类

实体类需实现UserDetails,UserDetails对象包含了一系列在验证时会用到的信息,包括用户名、密码、权限以及其他信息,SpringSecurity会根据这些信息判断验证是否成功

package com.example.pojo;

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

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

public class User implements UserDetails {

    private Long id;
    private String username;
    private String password;
    private String roles;
    private boolean enable;
    private List<GrantedAuthority> authorities;

    public Long getId() {
        return id;
    }

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

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

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

    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }

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

    public void setAuthorities(List<GrantedAuthority> authorities) {
        this.authorities = authorities;
    }

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

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

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

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

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

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

    @Override
    public boolean isEnabled() {
        return this.enable;
    }
}

POJO实现了UserDetails,其中的一些方法,比如:isAccountNonLocked、isAccountNonLocked和isCredentialsNonExpired这里暂时没用到,统一返回为true,否则会被SpringSecurity认为是账号异常

UserDetailService的实现

UserDetailService定义了一个loadUserByUsername方法,用于获取UserDetails对象

package com.example.service;

import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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;

@Service
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //从数据库读取
        User user = userMapper.findByUserName(username);
        if(null == user){
            throw new UsernameNotFoundException("用户不存在");
        }
        //将roles解析为UserDetails的权限集
        user.setAuthorities(generateAuthorities(user.getRoles()));
        return user;
    }

    private List<GrantedAuthority> generateAuthorities(String roles){
        List<GrantedAuthority> authorities= new ArrayList<>();
        String[] roleArrays = roles.split(",");
        if(roles != null && !"".equals(roles)){
            for (String role : roleArrays) {
                authorities.add(new SimpleGrantedAuthority(role));
            }
        }
        return authorities;
    }
}

因为SpringSecurity使用的是5.x.x版本,对应的SpringSecurity对明文密码设置有要求,否则会报空指针异常
Spring Security实战--(二)数据库实现_第4张图片
PasswordEncoder配置

这里对PasswordEncoder做自定义类,做明文密码匹配

@Service
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配置类

在WebSecurityConfig配置类中进行配置添加,使用自定义的UserDetailService和PasswordEncoder如下:

@Autowired
private MyUserDetailService myUserDetailService;

@Autowired
private MyPasswordEncoder myPasswordEncoder;

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

Mapper配置

这里只用配置一个接口就行了,数据库的连接配置就不放出来了

package com.example.mapper;

import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

@Component
public interface UserMapper {

    @Select("SELECT * FROM USERS WHERE USERNAME = #{username}")
    User findByUserName(@Param("username") String  username);
}

三、页面验证

输入http://localhost:8080/user/api/hello,会自动跳转到登录界面
Spring Security实战--(二)数据库实现_第5张图片
输入用户user,密码123456
在这里插入图片描述
输入http://localhost:8080/admin/api/hello
Spring Security实战--(二)数据库实现_第6张图片
显示权限不够,改用admin账户登录;建立使用无痕模式测试,以免浏览器缓存导致奇奇怪怪的问题

在这里插入图片描述
再登入http://localhost:8080/user/api/hello
在这里插入图片描述
admin和user路径都可以访问

以上就是数据库实现Spring Security的自定义访问

你可能感兴趣的:(Spring,Security)