Security

SpringSecurity(安全)

在web开发者,安全第一位!过滤器,拦截器!

shiro,SpringSecurity 认证,授权

  • 功能权限
  • 访问权限
  • 菜单权限
简介

SpringSecurity是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,我们仅需引入spring-boot-starter-security模块,进行少量的配置。

核心类:

  • WebSecuriyConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security两个目标,认证(Authentication)和授权(Authorization)

官网:https://spring.io/projects/spring-security/

官方文档:https://docs.spring.io/spring-security/site/docs/5.4.5/reference/html5/

Spring Security环境搭建

依赖
    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!--thymeleaf security整合包-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>

        <!--thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
Controller
@Controller
public class RouterController {
    @RequestMapping({"/", "/index"})
    public String index() {
        return "index";
    }

    @RequestMapping({"/toLogin"})
    public String toLogin() {
        return "views/login";
    }

    @RequestMapping({"/level1/{id}"})
    public String toLevel1(@PathVariable("id") int id) {
        return "views/level1/" + id;
    }

    @RequestMapping({"/level2/{id}"})
    public String toLevel2(@PathVariable("id") int id) {
        return "views/level2/" + id;
    }

    @RequestMapping({"/level3/{id}"})
    public String toLevel3(@PathVariable("id") int id) {
        return "views/level3/" + id;
    }
}
Security配置类
//Aop 拦截器
@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");
        //没有权限会到登陆页面
        http.formLogin();
        //开启注销功能,注销成功到主页,此处也可以做删除cookies和session操作
        http.logout().logoutSuccessUrl("/");
    }

    //认证,Springboot 2.1.x之后版本会报错,密码没有编码There is no PasswordEncoder
    //密码编码:PasswordEncoder
    //spring security5.0有很多加密方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/*        auth.inMemoryAuthentication()
                .withUser("jpy").password("123456").roles("vip2", "vip3")
                .and()
                .withUser("admin").password("123456").roles("vip1", "vip2", "vip3")
                .and()
                .withUser("guest").password("123456").roles("vip1");*/
        //从内存中,也可以从数据库jdbc
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("jpy").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2", "vip3")
                .and()
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2", "vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");

    }
}
前端

此时用户名显示不出来,是因为springboot版本过高,只有2.1.x之前才支持

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item" th:href="@{/index}">首页</a>

            <!--登录注销-->
            <div class="right menu">
                <!--如果未登录,显示登陆按钮-->
                <!--未登录-->
                <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>
                    </a>
                </div>
                <!--注销-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item" th:href="@{/logout}">
                        <i class="sign-out icon"></i> 注销
                    </a>
                </div>
            </div>
        </div>
    </div>

改变springboot依赖为2.0.9,可以显示用户名,但注销时会报404错误。注销通过get请求,不安全,所以报错,通过配置类以下配置,关闭放攻击。

//关闭防攻击
http.csrf().disable();
根据用户动态显示菜单

通过标签sec:authorize="hasRole('vip1')"

        <div class="ui three column stackable grid">
            <!--根据用户动态显示菜单-->
            <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>

            <div class="column" sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column" sec:authorize="hasRole('vip3')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
开启记住我功能
//记住我,cookies实现,默认保存两周
http.rememberMe();
定制登陆页,自定义记住我
        <div class="ui placeholder segment">
            <div class="ui column very relaxed stackable grid">
                <div class="column">
                    <div class="ui form">
                        <!--1.login-->
                        <form th:action="@{/login}" method="post">
                            <div class="field">
                                <label>Username</label>
                                <div class="ui left icon input">
                                    <input type="text" placeholder="Username" name="user">
                                    <i class="user icon"></i>
                                </div>
                            </div>
                            <div class="field">
                                <label>Password</label>
                                <div class="ui left icon input">
                                    <input type="password" name="pwd">
                                    <i class="lock icon"></i>
                                </div>
                            </div>

                            <div class="field">
                                <input type="checkbox" name="remember">记住我
                            </div>
                            <input type="submit" class="ui blue submit button"/>
                        </form>
                    </div>
                </div>
            </div>
        </div>

自定义loginPage为toLogin时如果login.html中form表单提交路径和自定义的toLogin不一样,就必须手手动配置把登陆页面路径配置到loginProcessingUrl("/login")

登陆页面表单用户名不是username和password,就必须吧表单中参数配置进usernameParameter和passwordParameter

http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");

自定义记住我,rememberMeParameter中参数必须和登陆页面自己写的记住我input框的name一样

http.rememberMe().rememberMeParameter("remember");
posted @   jpy  阅读(190)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示