springsecurity的相关知识(1)

ps:此篇是我根据编程不良人的springsecurity学习视频学习的笔记
地址如下:https://www.bilibili.com/video/BV1z44y1j7WZ?p=28&spm_id_from=pageDriver&vd_source=c5dc98a1da2912992b967e875b197e56

加了这个依赖之后,不用做任何操作,就会对系统进行保护

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

输入写好的hello地址,会自动跳转到login地址

登录hello地址

在这里插入图片描述

直接跳转到登录login页面

在这里插入图片描述

原理(省略,以后再细看)

是因为springsecurity内置的一系列filter过滤器实现的
因为springbootWebSecurityConfiguration里面对所有请求都做了权限控制
这个是默认配置
在这里插入图片描述

自带登陆界面

在这里插入图片描述

UserDetailService

在这里插入图片描述
我们的需求是公共资源不限制,可以直接访问,而显然springsecurity不符合我们的需求。
在这里插入图片描述

自定义认证(WebSecurityConfigurerAdapter)

可以继承WebSecurityConfigureAdapter去重写configure方法,去自定义资源权限的规则,去完成一个不同的请求权限处理
在这里插入图片描述
上面图片的代码实现

@Configuration
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //表示放行
                .mvcMatchers("/index").permitAll()//放行资源要写在任何前面
                //其他都要认证
                .anyRequest().authenticated()
                .and()
                //开启表单认证,就是上面一开始说的login页面
                .formLogin();
    }
}

自定义登陆页面(loginPage)

原本是字符串拼接的响应界面
在这里插入图片描述

使用了 loginPage(“/login.html”)来自定义登录界面,并且在template中新建了一个login.html完成后如下图,登陆界面改变了。

新建一个login.html
在这里插入图片描述

@Configuration
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //表示放行
                .mvcMatchers("/login.html").permitAll()
                .mvcMatchers("/index").permitAll()//放行资源要写在任何前面
                //其他都要认证
                .anyRequest().authenticated()
                .and()
                //表单
                .formLogin()
                //自定义登陆界面
                .loginPage("/login.html")
                .loginProcessingUrl("/dologin")//指定登录的url
 //               .successForwardUrl("/hello") //认证成功后 forward跳转到hello路径  始终在认证成功后跳转到指定请求
//                .defaultSuccessUrl("/index") //认证成功后 redirect跳转到index  根据上一次保存的请求重新跳转
                .successHandler(new MyAuthenticationSuccessHandler())//认证成功处理前后端分离
                .and()
                .csrf().disable();//禁止csrf跨站请求保护
    }
}

在这里插入图片描述

自定义认证成功跳转逻辑(MyAuthenticationSuccessHandler)

前后端不分离,使用successForwardUrl和defalutSuccessUrl,这样登陆成功就可以指定到你需要的页面

在这里插入图片描述

前后端分离,使用successHandler,需要传一个AuthenticationSuccessHandler,这时候我们就不是跳转某个路径,而是返回一个接口。这就需要我们去新建一个MyAuthenticationSuccessHandler类去实现AuthenticationSuccessHandler接口,并重写onAuthenticationSuccess方法

在这里插入图片描述

/*
自定义认证成功之后的处理
 */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

        HashMap<String, Object> result = new HashMap<>();
        result.put("msg","登陆成功");
        result.put("status",200);
        result.put("authentication",authentication);
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        httpServletResponse.getWriter().println(s);

    }
}

定义认证失败跳转逻辑

forward:跳转是存在request作用域中的,登录页面的url是不变化的
redirect:重定向是存在session中的,登录页面的url是变化的,如下图所示

//                .failureForwardUrl("/login.html") //认证失败后 forward跳转到login路径  始终在认证失败后跳转到指定请求 存在request
                .failureUrl("/login.html")//认证失败后 redirect跳转到login  根据上一次保存的请求重新跳转 存在session

使用forward一开始的url是:localhost:8000/login.html

在这里插入图片描述

之后是/dologin

在这里插入图片描述
使用redirect一开始的url是:localhost:8000/login.html,如上图一致
之后是/login.html,是变化成我们设置的
在这里插入图片描述

自定义前后端分离认证失败跳转逻辑(failureHandler)

在这里插入图片描述

前后端分离,使用failureHandler,需要传一个AuthenticationFailureHandler,这时候我们就不是跳转某个路径,而是返回一个接口。这就需要我们去新建一个MyAuthenticationFailureHandler类去实现AuthenticationFailureHandler接口,并重写onAuthenticationfailure方法,返回的是json字符串

/**
 * 自定义认证失败
 */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("msg","登陆失败"+e.getMessage());
        hashMap.put("status",500);
        response.setContentType("application/json;charset=utf-8");
        String s=new ObjectMapper().writeValueAsString(hashMap);
        response.getWriter().println(s);

    }
}

返回的是json字符串

在这里插入图片描述

定义认证成功后注销(logoutUrl)

这些不设置的话,也是默认设置成这样的

在这里插入图片描述

前后端分离自定义认证成功后注销

写一个类去实现LogoutSuccessHandler,重写 onLogoutSuccess方法,返回给前端一个json对象,成功后如下图

/**
 * 自定义注销成功处理
 */
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        HashMap<String, Object> result = new HashMap<>();
        result.put("msg","注销成功,当前认证对象为:"+authentication);
        result.put("status",200);
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        httpServletResponse.getWriter().println(s);
    }
}

注销默认的url是/logout
在这里插入图片描述

登录用户数据获取(SecurityContextHolder)

在这里插入图片描述

 @RequestMapping(value = "/hello")
    public String hello(){
        System.out.println("hello");
        //获取认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) authentication.getPrincipal();
        System.out.println("身份信息:"+user.getUsername());
        System.out.println("权限信息:"+authentication.getAuthorities());
        return "hello";

    }

输出结果,权限没设置所以没有

在这里插入图片描述

自定义认证数据源

认证的核心类AuthenticationManager
增加自定义UserDetailsService实现类 将默认从内存中获取的数据改为数据库中获取

使用springboot对security默认配置中,在工厂中默认创建AuthenticationManager,我们只需要去配置一个Bean,userDetailsService(),去配置上身份信息,AuthenticationManagerBuilder会去检测你当前工厂中有没有userDetailsService,有就会自动给springboot对security默认配置中。我们不需要再去默认配置中写userDetailsService。

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


    //springboot对security默认配置中,在工厂中默认创建AuthenticationManager
    @Override
    protected void initialize(AuthenticationManagerBuilder builder) {
        System.out.println("springboot默认配置"+builder);
    }

第二种是自定义AuthenticationManager,我们在工厂中虽然配置了userDetailsService的Bean,但是会检测不到,因为自定义里面,已经把工厂里面的覆盖掉了,我们需要去手动告诉他auth.userDetailsService(userDetailsService()),去使用我们工厂中的userDetailsService

 //自定义AuthenticationManager
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("自定义AuthenticationManager"+auth);
        auth.userDetailsService(userDetailsService());
    }

默认全局AuthenticationManager

在这里插入图片描述

自定义全局AuthenticationManager

在这里插入图片描述

 //自定义AuthenticationManager,并没有在工厂中暴露出来
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("自定义AuthenticationManager"+auth);
        auth.userDetailsService(userDetailsService());
    }

    //用来在自定义的AuthenticationManager在工厂中暴露,可以在任何位置注入
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception{
        return super.authenticationManagerBean();
    }

自定义数据源实现认证

  1. 建立数据库表
-- 用户表
CREATE TABLE `user`
(
`id` int(11)NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
`enabled` TINYINT(1) DEFAULT NULL,
`accountNonExpired` TINYINT(1) DEFAULT NULL,
`accountNonLocked` TINYINT(1) DEFAULT NULL,
`credentialsNonExpired` TINYINT(1) DEFAULT NULL,
PRIMARY KEY(`id`)

)ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 角色表
CREATE TABLE `role`
(
`id` int(11)NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) DEFAULT NULL,
`name_zh` VARCHAR(32) DEFAULT NULL,

PRIMARY KEY(`id`)

)ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 用户角色关系表
CREATE TABLE `user_role`
(
`id` int(11)NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,

PRIMARY KEY(`id`),
KEY `uid`(`uid`),
KEY `	rid`(`rid`)

)ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;


-- 插入用户数据

   INSERT INTO `user`
   VALUES(1,'root','{noop}123',1,1,1,1);
   
   INSERT INTO `user`
   VALUES(2,'admin','{noop}123',1,1,1,1);
   
   INSERT INTO `user`
   VALUES(3,'song','{noop}123',1,1,1,1);

   
   
-- 插入角色数据


   INSERT INTO `role`
   VALUES(1,'ROLE_product','商品管理员');
   
   INSERT INTO `role`
   VALUES(2,'ROLE_admin','系统管理员');
   
   INSERT INTO `role`
   VALUES(3,'ROLE_user','用户管理员');

   
-- 插入用户角色数据

   INSERT INTO `user_role`
   VALUES(1,1,1);
   
   INSERT INTO `user_role`
   VALUES(2,1,2);
   
   INSERT INTO `user_role`
   VALUES(3,2,2);
   INSERT INTO `user_role`
   VALUES(4,3,3);


User表
在这里插入图片描述
Role表
在这里插入图片描述
user_role表
在这里插入图片描述

新建User类,需要实现UserDetails接口,新建role类

/**
 * 自定义User
 */
//需要实现UserDetailService,需要返回一个UserDetail,为了能对应上所以实现UserDetail
public class User implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;//账户是否激活
    private Boolean accountNonExpired;//账户是否过期
    private Boolean accountNonLocked;//账户是否锁定
    private Boolean credentialsNonExpired;//密码是都过期
    private List<Role> role=new ArrayList<>();//关系属性,用来存储当前用户所有角色信息

    //返回权限信息
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<SimpleGrantedAuthority> auth = new HashSet<>();
        role.forEach(role->{
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getName());
            auth.add(simpleGrantedAuthority);
        });
        return null;
    }

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

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

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

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

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

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

    public Integer getId() {
        return id;
    }

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

    public List<Role> getRole() {
        return role;
    }

    public void setRole(List<Role> role) {
        this.role = role;
    }
}


//角色类
@Data
public class Role {
    private Integer id;
    private String name;
    private String nameZh;


}

新建一个MyUserDetailService类去实现UserDetailsService,去重写loadUserByUsername,返回一个User对象

@Component
public class MyUserDetailService implements UserDetailsService {

    //doa--springboot---mybatis
  @Autowired
    UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.loadUserByUserName(username);
        //查询用户

        if(ObjectUtils.isEmpty(user))throw new UsernameNotFoundException("用户名不正确");
        //查询权限信息
        List<Role> roleByUid = userDao.getRoleByUid(user.getId());
        user.setRole(roleByUid);
        return user;
    }
}

新建一个Doa层还有相应的mapper.xml

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springsecuritytest.com.hb.Dao.UserDao">


    <select id="loadUserByUserName" resultType="com.example.springsecuritytest.com.hb.entity.User">
        select id,
               username,
               password,
               enabled,
               accountNonExpired,
               accountNonLocked,
               credentialsNonExpired
        from user
        where username=#{username}
    </select>
    <select id="getRoleByUid" resultType="com.example.springsecuritytest.com.hb.entity.Role">
        select r.id,
               r.name,
               r.name_zh nameZh
        from role r,
             user_role ur
        where r.id=ur.rid
        and ur.uid=#{uid}
    </select>
</mapper>

最后,要在配置类中,将自定义的AuthenticationManager的userDetailService换成自己配置的

在这里插入图片描述

结果:都是自己数据库存储的信息

在这里插入图片描述

ps:根据编程不良人的springsecurity学习视频学习
地址如下:https://www.bilibili.com/video/BV1z44y1j7WZ?p=28&spm_id_from=pageDriver&vd_source=c5dc98a1da2912992b967e875b197e56

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值