用最笨的方法填鸭式实现spirngboot+SpringSecurity

表关系

在这里插入图片描述
这里很简单 看起来乱
白话说就是一个用户 可以有很多角色 一个用户表 对应多个角色表
一个角色 可以有很多权限 一个角色表对应多个权限表
中间两个表为连接表 因为我们并没有外键直接指向对应表
所以我们就需要join 连接查询 类似通过角色表id查出连接表对应的字段
再从连接表对应的字段拿出角色id 匹配对应角色 巴拉巴拉的 你懂得

进入正题

依赖

<dependencies>
		<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity4</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

第一步骤

创建一个SecurityConfig 继承 WebSecurityConfigurerAdapter
重写里面

  • protected void configure(HttpSecurity http)

  • protected void configure(AuthenticationManagerBuilder auth)

两个方法

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
  private   userDetailsServiceImpl userDetailsService;





    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        //定制请求的授权规则
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasAuthority("chuji")
                .antMatchers("/level2/**").hasAuthority("zhongji")
                .antMatchers("/level3/**").hasAuthority("gaoji");

        //开启自动配置的登陆功能,效果,如果没有登陆,没有权限就会来到登陆页面
        http.formLogin().usernameParameter("user").passwordParameter("pwd")
                .loginPage("/userlogin");
        //1、/login来到登陆页
        //2、重定向到/login?error表示登陆失败
        //3、更多详细规定
        //4、默认post形式的 /login代表处理登陆
        //5、一但定制loginPage;那么 loginPage的post请求就是登陆


        //开启自动配置的注销功能。
        http.logout().logoutSuccessUrl("/");//注销成功以后来到首页
        //1、访问 /logout 表示用户注销,清空session
        //2、注销成功会返回 /login?logout 页面;

        //开启记住我功能
        http.rememberMe().rememberMeParameter("remeber");
        //登陆成功以后,将cookie发给浏览器保存,以后访问页面带上这个cookie,只要通过检查就可以免登录
        //点击注销会删除cookie

    }

    //定义认证规则
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        super.configure(auth);

      auth.userDetailsService(userDetailsService());

    }

第二步

创建一个bean 类 也叫做model 来实现 UserDetails 至于为啥实现这个类看下去就知道了

//用户表对应实体类
public class SysUser implements Serializable {

	private static final long serialVersionUID = -6525908145032868837L;
    private Long id;
	private String username;
	private String password;
	private String nickname;
	private String headImgUrl;
	private String phone;
	private String telephone;
	private String email;
	@JsonFormat(pattern = "yyyy-MM-dd")
	private Date birthday;
	private Integer sex;
	private Integer status;
	private String intro;

实现UserDetails 类 这里是继承了用户表实体类

public class LoginUser extends SysUser implements UserDetails {

    private List<Permission>  list;

    public List<Permission> getList() {
        return list;
    }

    public void setList(List<Permission> list) {
        this.list = list;
    }
 //权限列表
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        return  list.stream().filter(p->!StringUtils.isEmpty(p.getPermission())).map(p-> new SimpleGrantedAuthority(p.getPermission()))
                .collect(Collectors.toList());
    }
// 账户是否未过期
    @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 Collection<? extends GrantedAuthority> getAuthorities()
这个方法很重要使SpringSecurity 会自动调用拿出来里面的权限
可能使用流写法比较难懂毕竟我们都是菜鸟嘛
所以对应为for循环的代码就出现了

  list.stream().filter(p->!StringUtils.isEmpty(p.getPermission())).map(p-> new SimpleGrantedAuthority(p.getPermission()))
                .collect(Collectors.toList());
 上面代码等同于下面代码
-------------------------------------------------------------------------------

 HashSet<SimpleGrantedAuthority> set  =  new HashSet<SimpleGrantedAuthority>();
        for (Permission p: list) {
            if (p.getPermission() !=null) {
                set.add(new SimpleGrantedAuthority(p.getPermission()));
            }
        }

第三部创建dao层

@Mapper
public interface PermissionDao {

    /*
    * 通过用户id获取权限
    * */

    @Select("select p.* from sys_permission p inner " +
            "join sys_role_permission rp on p.id=rp.permissionId  inner join sys_role_user ru on ru.roleId = rp.roleId where ru.userId = #{userId}")
   List<Permission> listByUserId(Long userId);
}

@Mapper
public interface SysUserDao {
    //通过账号获取用户
    @Select("select * from sys_user t where t.username = #{username}")
    SysUser getUser(String username);

}

第四步

创建一个 service 继承 UserDetailsService 框架需要来判断是否登录 相当于我们自己写的 判断登录service 不过框架给你实现了 收了框架的好 拿人手短
就得听从框架的规矩 实现 这个UserDetailsService

@Service
public class userDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private   SysUserDao sysUserDao;
    @Autowired
    private  PermissionDao permissionDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("loadUserByUsername");

        //按照名字查询用户
        SysUser sysUser = sysUserDao.getUser(username);
        if (sysUser == null) {
            throw new AuthenticationCredentialsNotFoundException("用户名不存在");
        }

        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(sysUser, loginUser);
        //通过用户id返回权限表
        List<Permission> permissions = permissionDao.listByUserId(sysUser.getId());
        loginUser.setList(permissions);
        System.out.println("用户"+sysUser.toString());
        System.out.println("权限列表"+permissions.toString());
        return loginUser;
    }

public UserDetails loadUserByUsername(String username)
在这里方法只有一个那就是 loadUserByUsername 但是注意返回的类型为UserDetails这像不像我们第二步实现的 UserDetails 类 正好返回他
看上面代码可以看出loadUserByUsername 的实现其实就是

  1. 通过username 账号取出我们的用户表user的信息
  2. 通过user的id取出我们的权限信息
  3. new了一个UserDetails 接口的实现类我们自己实现的类loginUse
  4. 把用户信息存入loginUse
  5. 把权限信息传入loginUse
  6. 返回给SpringSecurity
    经过上面spring就拿到我们的权限信息和用户信息了 之后spirng会通过这些信息来进行密码比较 权限比较此时此刻我们可以说高枕无忧了

源码地址

[https://gitee.com/jia_yongli/springboot-05-security.git]
数据库文件
用户表测试数据
在这里插入图片描述
这两个为账号密码为测试数据其他为另外的项目

角色数据
在这里插入图片描述
权限数据
在这里插入图片描述
为null的都是用不到的字段

posted @ 2021-08-24 15:45  呆呆的木鸡  阅读(50)  评论(0)    收藏  举报