zoukankan      html  css  js  c++  java
  • springboot整合springsecurity最完整,只看这一篇就够了

    本人结合其他博客和自己查询的资料,一步一步实现整合了security安全框架,其中踩过不少的坑,也有遇到许多不懂的地方,为此做个记录。

    开发工具:ide、数据库:mysql5.7、springboot版本:2.3.7

    个人对Spring Security的执行过程大致理解(仅供参考)

     

    使用Spring Security很简单,只要在pom.xml文件中,引入spring security的依赖就可以了

    pom配置:

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

    这个时候我们不在配置文件中做任何配置,随便写一个Controller 

    @RestController
    public class TestController {
        @GetMapping("/hello")
        public String request() {
            return "hello";
        }
    }

    启动项目,我们会发现有这么一段日志

    此时表示Security生效,默认对项目进行了保护,我们访问该Controller中的接口(http://localhost:8080/securitydemo/hello),会见到如下登录界面(此界面为security框架自带的默认登录界面,后期不用可以换成自定义登录界面)

     这里面的用户名和密码是什么呢?此时我们需要输入用户名:user,密码则为之前日志中的"19262f35-9ded-49c2-a8f6-5431536cc50c",输入之后,我们可以看到此时可以正常访问该接口

     

    在老版本的Springboot中(比如说Springboot 1.x版本中),可以通过如下方式来关闭Spring Security的生效,但是现在Springboot 2中已经不再支持

    security:
      basic:
        enabled: false

    springboot2.x后可以在启动类中设置

    1、配置基于内存的角色授权和认证信息

      1.1目录

      

      1.2 WebSecurityConfg配置类

      Spring Security的核心配置类是 WebSecurityConfigurerAdapter抽象类,这是权限管理启动的入口,这里我们自定义一个实现类去实现它。

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description SpringSecurity安全框架配置
     */
    @Configuration
    @EnableWebSecurity//开启Spring Security的功能
    //prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            /**
            * 基于内存的方式,创建两个用户admin/123456,user/123456
            * */
            auth.inMemoryAuthentication()
                    .withUser("admin")//用户名
                    .password(passwordEncoder().encode("123456"))//密码
                    .roles("ADMIN");//角色
            auth.inMemoryAuthentication()
                    .withUser("user")//用户名
                    .password(passwordEncoder().encode("123456"))//密码
                    .roles("USER");//角色
        }
    
        /**
         * 指定加密方式
         */
        @Bean
        public PasswordEncoder passwordEncoder(){
            // 使用BCrypt加密密码
            return new BCryptPasswordEncoder();
        }
    }

      1.3 MainController控制器接口

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description 主控制器
     */
    @RestController
    public class MainController {
    
        @GetMapping("/hello")
        public String printStr(){
            System.out.println("hello success");
            return "Hello success!";
        }
    
    }

    这样重新运行后我们就可以通过admin/123456、user/123456两个用户登录了。

    当然,你也可以基于配置文件创建用户账号,在pom.xml中添加:

     2、配置基于数据库的认证信息和角色授权

      2.1 目录

       2.2  CustomUserDetailsService实现类

    UserDetailsService是需要实现的登录用户查询的service接口,实现loadUserByUsername()方法,这里我们自定义CustomUserDetailsService实现类去实现UserDetailsService接口

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description
     */
    
    @Component
    public class CustomUserDetailsService implements UserDetailsService {
        @Resource
        private UserInfoService userInfoService;
        @Resource
        private PasswordEncoder passwordEncoder;
        @Override
        public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
            /**
             * 1/通过userName 获取到userInfo信息
             * 2/通过User(UserDetails)返回details。
             */
            //通过userName获取用户信息
            UserInfo userInfo = userInfoService.getUserInfoByUsername(userName);
            if(userInfo == null) {
                throw new UsernameNotFoundException("not found");
            }
            //定义权限列表.
            List<GrantedAuthority> authorities = new ArrayList<>();
            // 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
            authorities.add(new SimpleGrantedAuthority("ROLE_"+ userInfo.getRole()));
            User userDetails = new User(userInfo.getUserName(),passwordEncoder.encode(userInfo.getPassword()),authorities);
            return userDetails;
        }
    }
    WebSecurityConfg配置类:
    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description SpringSecurity安全框架配置
     */
    @Configuration
    @EnableWebSecurity//开启Spring Security的功能
    //prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
        /**
         * 指定加密方式
         */
        @Bean
        public PasswordEncoder passwordEncoder(){
            // 使用BCrypt加密密码
            return new BCryptPasswordEncoder();
        }
    }

    对于通过userName获取用户信息的服务层,持久层和数据库语句就不介绍了,这里使用的是SSM框架,使用mybaits。

      2.3 数据库设计

     

     角色表 roles

    用户表 user

    用户角色关系表 roles_user

     3、自定义表单认证登录

      3.1 目录

      

       3.2  WebSecurityConfg核心配置类

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description spring-security权限管理的核心配置
     */
    @Configuration
    @EnableWebSecurity//开启Spring Security的功能
    //prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
    
        @Resource
        private AuthenticationSuccessHandler loginSuccessHandler; //认证成功结果处理器
        @Resource
        private AuthenticationFailureHandler loginFailureHandler; //认证失败结果处理器
    
        //http请求拦截配置
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.headers().frameOptions().disable();//开启运行iframe嵌套页面
    
            http//1、配置权限认证
                .authorizeRequests()
                    //配置不拦截路由
                    .antMatchers("/500").permitAll()
                    .antMatchers("/403").permitAll()
                    .antMatchers("/404").permitAll()
                    .antMatchers("/login").permitAll()
                    .anyRequest() //任何其它请求
                    .authenticated() //都需要身份认证
                    .and()
                 //2、登录配置表单认证方式
                .formLogin()
                    .loginPage("/login")//自定义登录页面的url
                    .usernameParameter("username")//设置登录账号参数,与表单参数一致
                    .passwordParameter("password")//设置登录密码参数,与表单参数一致
                    // 告诉Spring Security在发送指定路径时处理提交的凭证,默认情况下,将用户重定向回用户来自的页面。登录表单form中action的地址,也就是处理认证请求的路径,
                    // 只要保持表单中action和HttpSecurity里配置的loginProcessingUrl一致就可以了,也不用自己去处理,它不会将请求传递给Spring MVC和您的控制器,所以我们就不需要自己再去写一个/user/login的控制器接口了
                    .loginProcessingUrl("/user/login")//配置默认登录入口
                    .defaultSuccessUrl("/index")//登录成功后默认的跳转页面路径
                    .failureUrl("/login?error=true")
                    .successHandler(loginSuccessHandler)//使用自定义的成功结果处理器
                    .failureHandler(loginFailureHandler)//使用自定义失败的结果处理器
                    .and()
                //3、注销
                .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessHandler(new CustomLogoutSuccessHandler())
                    .permitAll()
                    .and()
                //4、session管理
                .sessionManagement()
                    .invalidSessionUrl("/login") //失效后跳转到登陆页面
                    //单用户登录,如果有一个登录了,同一个用户在其他地方登录将前一个剔除下线
                    //.maximumSessions(1).expiredSessionStrategy(expiredSessionStrategy())
                    //单用户登录,如果有一个登录了,同一个用户在其他地方不能登录
                    //.maximumSessions(1).maxSessionsPreventsLogin(true) ;
                    .and()
                //5、禁用跨站csrf攻击防御
                .csrf()
                    .disable();
        }
        
        @Override
        public void configure(WebSecurity web) throws Exception {
            //配置静态文件不需要认证
            web.ignoring().antMatchers("/static/**");
        }
    
        /**
         * 指定加密方式
         */
        @Bean
        public PasswordEncoder passwordEncoder(){
            // 使用BCrypt加密密码
            return new BCryptPasswordEncoder();
        }
    }

    踩坑点1:登录页面接口/login和登录验证接口/user/login,这里是自己之前一直搞错的重点,这里就用网上的图片展示了

    踩坑点2:springboot配置spring security 静态资源不能访问

    security的配置:在类WebSecurityConfig继承WebSecurityConfigurerAdapter,这个类是我们在配置security的时候,对我们请求的url及权限规则的一些认证配置。具体的不说了,这里主要是静态资源的问题。

    在这个类中我们会重写一些方法,其中就有一个方法,可以为我们配置一下静态资源不需要认证。

    @Override
        public void configure(WebSecurity web) throws Exception {
            //配置静态文件不需要认证
            web.ignoring().antMatchers("/static/**");
        }

    页面的引用如下:

     <link rel="stylesheet" th:href="@{static/layui/css/layui.css}">

    之后我们启动项目:看到css并没有生效

     这时候仅仅通过spring security配置是不够的,我们还需要去重写addResourceHandlers方法去映射下静态资源,这个方法应该很熟悉了,我们通过springboot添加拦截器的时候就会用到这个。

    写一个类WebMvcConfig继承WebMvcConfigurationSupport,注意spring boot2版本和1版本是不一样的,spring boot1版本继承的WebMvcConfigurerAdapter在spring boot2版本中已经提示过时了

    @Configuration
    public class WebMvcConfig extends WebMvcConfigurationSupport {
    
        /**
         * 配置静态资源
         * @param registry
         */
        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
            super.addResourceHandlers(registry);
        }
    }

    现在重新启动项目:css文件已经引用成功。

     3.3  ErrorPageConfig 配置错误页面

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description 配置错误页面 403 404 500  适用于 SpringBoot 2.x
     */
    @Configuration
    public class ErrorPageConfig {
    
        @Bean
        public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
            WebServerFactoryCustomizer<ConfigurableWebServerFactory> webCustomizer = new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
                @Override
                public void customize(ConfigurableWebServerFactory factory) {
                    ErrorPage[] errorPages = new ErrorPage[] {
                            new ErrorPage(HttpStatus.FORBIDDEN, "/403"),
                            new ErrorPage(HttpStatus.NOT_FOUND, "/404"),
                            new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500"),
                    };
                    factory.addErrorPages(errorPages);
                }
            };
            return webCustomizer;
        }
    }

     3.4 MainController 控制器

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description 主控制器
     */
    @Controller
    public class MainController {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @GetMapping("/login")
        public String loginPage(){
            System.out.println("login page");
            return "login";
        }
        @GetMapping("/index")
        @PreAuthorize("hasAnyRole('USER','ADMIN')")
        public String index(){
            System.out.println("index page");
            return "index";
        }
    
    
        @GetMapping("/admin")
        @PreAuthorize("hasAnyRole('ADMIN')")
        public String printAdmin(){
            System.out.println("hello admin");
            return "admin";
        }
    
        @GetMapping("/user")
        @PreAuthorize("hasAnyRole('USER','ADMIN')")
        public String printUser(){
            System.out.println("hello user");
            return "user";
        }
    
        /**
         * 找不到页面
         */
        @GetMapping("/404")
        public String notFoundPage() {
            return "/error/404";
        }
        /**
         * 未授权
         */
        @GetMapping("/403")
        public String accessError() {
            return "/error/403";
        }
        /**
         * 服务器错误
         */
        @GetMapping("/500")
        public String internalError() {
            return "/error/500";
        }
    }

    3.5 UserInfoController 用户控制器

    /**
     * @Author qt
     * @Date 2021/3/25
     * @Description
     */
    @Controller
    @RequestMapping("/user")
    public class UserInfoController {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Resource
        private UserInfoService userInfoService;
    
        @GetMapping("/getUserInfo")
        @ResponseBody
        public User getUserInfo(@RequestParam String username){
            return userInfoService.getUserInfoByUsername(username);
        }
    }

    SMM框架的其他部分就省略了,非这里重点。

    3.6 CustomAccessDecisionManager 自定义权限决策管理器

    /**
     * @Author qt
     * @Date 2021/3/31
     * @Description 自定义权限决策管理器
     */
    @Component
    public class CustomAccessDecisionManager implements AccessDecisionManager {
    
        /**
         * @Author: qt
         * @Description: 取当前用户的权限与这次请求的这个url需要的权限作对比,决定是否放行
         * auth 包含了当前的用户信息,包括拥有的权限,即之前UserDetailsService登录时候存储的用户对象
         * object 就是FilterInvocation对象,可以得到request等web资源。
         * configAttributes 是本次访问需要的权限。即上一步的 MyFilterInvocationSecurityMetadataSource 中查询核对得到的权限列表
         **/
        @Override
        public void decide(Authentication auth, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
            Iterator<ConfigAttribute> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (auth == null) {
                    throw new AccessDeniedException("当前访问没有权限");
                }
                ConfigAttribute ca = iterator.next();
                //当前请求需要的权限
                String needRole = ca.getAttribute();
                if ("ROLE_LOGIN".equals(needRole)) {
                    if (auth instanceof AnonymousAuthenticationToken) {
                        throw new BadCredentialsException("未登录");
                    } else
                        return;
                }
                //当前用户所具有的权限
                Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
                for (GrantedAuthority authority : authorities) {
                    if (authority.getAuthority().equals(needRole)) {
                        return;
                    }
                }
            }
            throw new AccessDeniedException("权限不足!");
        }
    
        @Override
        public boolean supports(ConfigAttribute configAttribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return true;
        }
    }

    3.7 CustomLogoutSuccessHandler 注销登录处理

    /**
     * @Author qt
     * @Date 2021/3/31
     * @Description 注销登录处理
     */
    public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            System.out.println("注销成功!");
            //这里写你登录成功后的逻辑
            response.setStatus(HttpStatus.OK.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("注销成功!");
        }
    }

    3.8 LoginFailureHandler 登录失败处理

    /**
     * @Author qt
     * @Date 2021/3/24
     * @Description 登录失败处理
     */
    @Component("loginFailureHandler")
    public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Resource
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            logger.info("登录失败");
            this.saveException(request, exception);
            this.getRedirectStrategy().sendRedirect(request, response, "/login?error=true");
        }
    }

     3.9 LoginSuccessHandler 登录成功处理

    /** @Author qt 
    * @Date 2021/3/24 * @Description 登录成功处理
    */ @Component("loginSuccessHandler") public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Resource private ObjectMapper objectMapper; private RequestCache requestCache; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { // 获取前端传到后端的全部参数 Enumeration enu = request.getParameterNames(); while (enu.hasMoreElements()) { String paraName = (String) enu.nextElement(); System.out.println("参数- " + paraName + " : " + request.getParameter(paraName)); } logger.info("登录认证成功"); //这里写你登录成功后的逻辑,可以验证其他信息,如验证码等。
    response.setContentType("application/json;charset=UTF-8"); JSONObject resultObj = new JSONObject(); resultObj.put("code", HttpStatus.OK.value()); resultObj.put("msg","登录成功"); resultObj.put("authentication",objectMapper.writeValueAsString(authentication)); response.getWriter().write(resultObj.toString()); this.getRedirectStrategy().sendRedirect(request, response, "/index");//重定向 } }

    3.10 login.html 登录页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
        <link rel="stylesheet" type="text/css" th:href="@{static/layui/css/layui.css}">
    </head>
    <body>
    <form method="POST" th:action="@{/user/login}">
        <div>
            用户名:<input type="text" name="username" id="username">
        </div>
        <div>
            密码:<input type="password" name="password" id="password">
        </div>
        <div>
             <button type="submit">立即登陆</button>
        </div>
        <!-- 以下为显示认证失败等提示信息(th:if=""一定要写 )-->
        <span style="color: red;" th:if="${param.error}" th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></span>
    </form>
    </body>
    </html>

     3.11 效果图片

    登录失败

     登录成功

     4、自定义ajax请求认证登录

    本人比较喜欢使用ajax的登录认证方式,这个比较灵活。

       4.1 目录

       4.2、较表单登录认证的改变

      LoginFailureHandler 登录失败处理

    /**
     * @Author qt
     * @Date 2021/3/24
     * @Description 登录失败处理
     */
    @Component("loginFailureHandler")
    public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Resource
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            logger.info("登录失败");
            response.setContentType("application/json;charset=UTF-8");
            //这里写你登录失败后的逻辑,可加验证码验证等
            String errorInfo = "";
            if (exception instanceof BadCredentialsException ||
                    exception instanceof UsernameNotFoundException) {
                errorInfo = "账户名或者密码输入错误!";
            } else if (exception instanceof LockedException) {
                errorInfo = "账户被锁定,请联系管理员!";
            } else if (exception instanceof CredentialsExpiredException) {
                errorInfo = "密码过期,请联系管理员!";
            } else if (exception instanceof AccountExpiredException) {
                errorInfo = "账户过期,请联系管理员!";
            } else if (exception instanceof DisabledException) {
                errorInfo = "账户被禁用,请联系管理员!";
            } else {
                errorInfo = "登录失败!";
            }
            logger.info("登录失败原因:" + errorInfo);
            //ajax请求认证方式
            JSONObject resultObj = new JSONObject();
            resultObj.put("code", HttpStatus.UNAUTHORIZED.value());
            resultObj.put("msg",errorInfo);
            resultObj.put("exception",objectMapper.writeValueAsString(exception));
            response.getWriter().write(resultObj.toString());
    
            //表单认证方式
            //this.saveException(request, exception);
            //this.getRedirectStrategy().sendRedirect(request, response, "/login?error=true");
        }
    }
    LoginSuccessHandler 登录成功处理
    /**
     * @Author qt
     * @Date 2021/3/24
     * @Description 登录成功处理
     */
    @Component("loginSuccessHandler")
    public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Resource
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
            response.setContentType("application/json;charset=UTF-8");
            // 获取前端传到后端的全部参数
              Enumeration enu = request.getParameterNames();
              while (enu.hasMoreElements()) {
                  String paraName = (String) enu.nextElement(); System.out.println("参数- " + paraName + " : " + request.getParameter(paraName));
              }
            logger.info("登录认证成功");
            //这里写你登录成功后的逻辑,可加验证码验证等
    
            //ajax请求认证方式
            JSONObject resultObj = new JSONObject();
            resultObj.put("code", HttpStatus.OK.value());
            resultObj.put("msg","登录成功");
            resultObj.put("authentication",objectMapper.writeValueAsString(authentication));
            response.getWriter().write(resultObj.toString());
    
            //表单认证方式
            //this.getRedirectStrategy().sendRedirect(request, response, "/index");//重定向
        }
    }

    login.html 登录页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
        <link rel="stylesheet" type="text/css" th:href="@{static/layui/css/layui.css}">
    </head>
    <body>
    <form method="POST" action="">
        <div>
            用户名:<input type="text" name="username" id="username">
        </div>
        <div>
            密码:<input type="password" name="password" id="password">
        </div>
        <div>
            <input type="button" name="login" id="login" th:value="立即登陆" onclick="mylogin()">
        </div>
    </form>
    
    <script type="text/javascript" charset="utf-8" th:src="@{static/jquery/jquery-3.5.1.min.js}"></script>
    <script type="text/javascript" charset="utf-8" th:src="@{static/layui/layui.js}"></script>
    <script th:inline="javascript" type="text/javascript">
        layui.use(['form','jquery','layedit', 'laydate'], function () {
            var $ = layui.jquery,
                form = layui.form,
                layer = layui.layer;
        });
        function mylogin() {
            var username = $("#username").val();
            var password = $("#password").val();
            console.log("username:" + username + "password:" + password);
            var index = layer.load(1);
            $.ajax({
                type: "POST",
                dataType: "json",
                url: "user/login",
                data: {
                    "username": username,
                    "password": password
                    //可加验证码参数等,后台登陆处理LoginSuccessHandler中会传入这些参数
                },
                success: function (data) {
                    layer.close(index);
                    console.log("data===>:" + JSON.stringify(data));
                    if (data.code == 200) { //登录成功
                        window.location.href = "index";
                    } else {
                        layer.msg(data.msg, {
                            icon: 2,
                            time: 3000 //2秒关闭(如果不配置,默认是3秒)
                        });
                    }
                },
                error: function () {
                    layer.close(index);
                    layer.msg("数据请求异常!", {
                        icon: 2,
                        time: 2000 //2秒关闭(如果不配置,默认是3秒)
                    });
                }
            });
        }
    </script>
    </body>
    </html>

    4.3 演示图片

    登录失败

     登录成功

     最后添加一个我写的一个小demo,里面也整合了security框架,使用springboot + ssm后端框架 + maven依赖包管理 + thmeleaf模板引擎 + pear-admin-layui前端框架等。

     demo演示地址:http://www.qnto.top/springfashionsys/login

     demo只对数据分析页面做了权限设置,只有admin才可访问。

    转载需要加链接哦,整理不易。

    总结:实践是检验真理的唯一标准,亲测可用。

     参考链接:

     https://blog.csdn.net/qq_40298902/article/details/106433192

     https://www.e-learn.cn/topic/3143567

     https://blog.csdn.net/qq_20108595/article/details/89647419

     http://www.spring4all.com/article/428

     https://blog.csdn.net/tanleijin/article/details/100698486

     https://blog.csdn.net/zhaoxichen_10/article/details/88713799

     https://blog.csdn.net/hanxiaotongtong/article/details/103095906

     https://www.jb51.net/article/140429.htm

     https://www.jianshu.com/p/29d10ad22531

     https://blog.csdn.net/weixin_39588542/article/details/110507502

     https://blog.csdn.net/sinat_33151213/article/details/89931819

  • 相关阅读:
    js概念理解
    web性能瓶颈
    http协议
    jquery插件开发
    Razor(cshtml)
    从局域网内的其他Linux主机下载文件
    Java多线程学习笔记
    java中String s="abc"及String s=new String("abc")详解
    Object中toString方法
    DAO层,Service层,Controller层、View层、entity层
  • 原文地址:https://www.cnblogs.com/qiantao/p/14605154.html
Copyright © 2011-2022 走看看