看完这篇,SpringSecurity就算入门了

看完这篇,SpringSecurity就算入门了

1.SpringSecurity是什么?能干嘛?

Spring Security 是一个安全框架,能够为 Spring企业应用系统提供声明式的安全访问控制。 主要用来做访问权限管理

Spring Security 基于 Servlet 过滤器、 IoC和AOP , 为 Web 请求和方法调用提供身份确认和授权处理,避免了代码耦合,减少了大量重复代码工作

它的两大功能? 认证和授权

  • 认证:识别并构建用户对象,如:根据请求中的username,获取登录用户的详细信息,判断用户状态,缓存用户对象到请求上下文等。(主要是登录注册部分)
  • 授权:判断用户能否访问当前请求,如:识别请求url,根据用户、权限和资源(url)的对应关系,判断用户能否访问当前请求url。(主要是权限管理)

知道了它是什么了,那么接下来我们就看看怎么用?

2.代码解读

首先我们这个是解耦的,基于javaConfig的开发模式,所以可以直接建一个Config类

2.1 SecurityConfig 初体验

大致随便看一个模板,拿来体验体验,不要求看懂,后面我们来细讲

//AOP切面编程,加个config就可以实现功能,不用改变代码
//WebSecurityConfigurerAdapter 自定义Security策略
//EnableWebSecurity   开启WebSecurity模式,@Enablexxxxx开启某个功能
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //链式编程
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人可以访问,功能页只有对应有权限的人才能访问
        //请求授权的规则
        http.authorizeRequests().antMatchers("/").permitAll()
                                .antMatchers("/level1/**").hasRole("vip1")
                                .antMatchers("/level2/**").hasRole("vip2")
                                .antMatchers("/level3/**").hasRole("vip3");

        //没有权限默认会到登录页,需要开启登录页面,定制登录页
        //可以直接把表单的提交地址改为Controller里面去主页的路径
        //loginProcessingUrl对应表单请求的路径,但是要求默认传值的name为username,password
        //可以利用.usernameParameter("user").passwordParameter("pwd")改默认的参数name值
        http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");

        //开启注销功能
        //logoutUrl 你要去哪里注销
//        http.logout().logoutSuccessUrl("/");

        //防止网站工具,post ,登陆失败可能的原因,但是我没降级用的 Springsecurity5 没问题
        http.csrf().disable();
        http.logout().logoutSuccessUrl("/");

        //开启记住我的功能 cookie默认2周,自定义接收前端记住我的参数
        http.rememberMe().rememberMeParameter("remember");
    }
    //认证,springboot.2.1.x,可以使用
    //密码编码:passwordEncoder
    //在Spring Security 5.0+ 新增了加密方法
    //AuthenticationManagerBuilder  自定义认证策略
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这些数据正常应该从数据库里读
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123"))
                .roles("vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123"))
                .roles("vip1","vip2","vip3").and()
                .withUser("youke").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
        ;
    }
}
2.2 起步依赖

首先我们想要使用就必要引入的依赖

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

当然如果是整合前端模板Themeleaf的话,还要引入两个依赖

        <!--thymeleaf模板-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
               <!--SpringSecurity+thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>
2.3 两大方法之授权

带有http的参数是授权方法

@EnableWebSecurity    //开启Security支持
public class SpringConfig extends WebSecurityConfigurerAdapter {

    @Override     //必须重写的方法,一个配置参数的方法
    protected void configure(HttpSecurity http) throws Exception{

    }
}
//这个是写在上面configure方法里面的
//首页所有人可以访问,功能页只有对应有权限的人才能访问
//请求授权的规则
http.authorizeRequests().antMatchers("/").permitAll()       //允许所有人可以访问/路劲
                        .antMatchers("/level1/**").hasRole("vip1")   //要拥有vip1权限的人才能访问/level1/**的路径,下面照着解读
                        .antMatchers("/level2/**").hasRole("vip2")
                        .antMatchers("/level3/**").hasRole("vip3");

没有权限默认会到登录页,需要开启登录页面,定制登录页

http.formLogin() 加了这个就会让没有认证的人去登录页面

//loginPage  去哪个页面登录  
//loginProcessingUrl对应登录表单请求的路径,但是要求前台默认传值的name为username,password
//如果不想写loginProcessingUrl,那么去哪个页面登录的那个表单action地址就要和这个loginPage路劲一样
//可以利用.usernameParameter("user").passwordParameter("pwd")改默认的参数username和password值
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
http.logout();  //开启注销

http.logout() 开启注销 ,他会自动删除cookie那些

值得注意的是登录登出,你开启的他的基本方法如果没有配置,就会走他自带的登录登出页面

http.logout() .logouturl("/logout") ; 这里就定义了去哪个页面注销

http.logout() .logoutSuccessUrl("/index"); 这里是自动注销了然后跳转去index页面

开启记住我

 //开启记住我的功能 cookie默认2周,自定义接收前端记住我的参数
 http.rememberMe().rememberMeParameter("remember");
2.4 两大方法之认证

带有AuthenticationMannagerBuilder的是认证

共同点都是,密码要加密 Security5.0后都要加密,而且为了安全最好加密密码 BCryptPasswordEncoder或者MD5之内的加密方式

2.4.1 认证之固定假值用户
    //认证,springboot.2.1.x,可以使用
    //密码编码:passwordEncoder
    //在Spring Security 5.0+ 新增了加密方法
    //AuthenticationManagerBuilder  自定义认证策略
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这些数据正常应该从数据库里读
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123"))
                .roles("vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123"))
                .roles("vip1","vip2","vip3").and()
                .withUser("youke").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
        ;
    }

上面withuser就是认证user这些人的信息,并且给予权限roles

2.4.2 认证之数据库查询

当然如果是数据库的话我们就需要再自己建一个类UserDetailService接口重写loadUserByUsername方法,固定写法,模板拿来即用

@Component
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserInfoService userInfoService;
    
    /**
     * 需新建配置类注册一个指定的加密方式Bean,或在下一步Security配置类中注册指定
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 通过用户名从数据库获取用户信息,这里就是调用数据库的接口
        UserInfo userInfo = userInfoService.getUserInfo(username);
        if (userInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 得到用户角色
        String role = userInfo.getRole();

        // 角色集合
        List<GrantedAuthority> authorities = new ArrayList<>();
        // 角色必须以`ROLE_`开头,数据库中没有,则在这里加
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role));

        return new User(
                userInfo.getUsername(),
             // 因为数据库是明文,所以这里需加密密码
                passwordEncoder.encode(userInfo.getPassword()),
                authorities
        );
    }
}

然后得到了用户信息之后就可以接轨上面的configure方法了

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDatailService userDatailService;   //固定注入这个

    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                 // 从数据库读取的用户进行身份认证
                .userDetailsService(userDatailService)   //用户信息
                .passwordEncoder(passwordEncoder());     //用户加密的密码
    }
}

上面设置完后,重新启动,在登录页面就可以输入数据库中的用户名/密码了。

随着登录的用户,伴随的权限管理

3. 前台细节

thymeleaf为例

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

sec:authorize="!isAuthenticated() 如果还没有登录,就显示登录按钮

sec:authorize="isAuthenticated() 如果登录了显示用户名和注销

sec:authentication=“name” 显示用户名

sec:authentication=“principal.authorities” 显示用户权限

                <!--未登录-->
                <div sec:authorize="!isAuthenticated()">
                    <a class="item" th:href="@{/toLogin}">
                        <i class="address card icon"></i> 登录
                    </a>
                </div>

                <!--已登录   显示用户名和注销-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        用户名:<span sec:authentication="name"></span>
                       角色:<span sec:authentication="principal.authorities"></span>
                    </a>
                    <a class="item" th:href="@{/logout}">
                        <i class="sign- out icon"></i> 注销
                    </a>
                </div>

那如何实现模块div之间对应权限的隐藏呢?

sec:authorize=“hasRole(‘vip1’)” 可以根据拥有此权限的用户才显示

<!--            菜单根据用户权限动态实现-->
            <div class="column" sec:authorize="hasRole('vip1')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

4.安全小细节

之前说了认证的时候要加密密码为了安全,这里还值得一题的是csrf防脚本攻击

//防止网站工具,post ,登陆失败可能的原因,但是我没降级用的 Springsecurity5 没问题
http.csrf().disable();

看到了这里差不多都理解了基本用法,接下来就是做一个小案例了,

提醒一下如果是前后端分离的项目,需要注意跨域问题

一个简单的跨域,可以之间在授权里面做

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 允许跨域访问
        http.cors();
    }

在这里插入图片描述

这样就可以实现跨域了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值