Spring security基本环境搭建

整个阴间版本,不推荐在项目里这样去使用,直接使用springboot就行了。这里只是为了更好的了解框架。
既有 xml 配置,也有servlet3.0 的方式加载,不过这2种方式都是基于spring注解来做的。

gitee地址:https://gitee.com/zzhua195/demo-spring-security
在这里插入图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zzhua</groupId>
    <artifactId>demo-spring-security</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
		
		<!-- jackson写出json -->
		<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>

        <!--整合spring-security-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>

     
    </dependencies>

    <build>
        <finalName>demo-spring-security</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <!-- 配置端口 -->
                        <port>8080</port>
                        <!-- 配置urlencoding -->
                        <uriEncoding>UTF-8</uriEncoding>
                        <!-- 配置项目的访问路径 -->
                        <path>/</path>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>


</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    <!-- 使用spring 监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Spring核心配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.zzhua.config.AppConfig</param-value>
    </context-param>

    <!-- 配置SpringMVC -->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>com.zzhua.config.CustomizeDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.zzhua.config.MyWebConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 代理过滤器 -->
    <filter>
    	<!-- 1、默认会获取filter的名字 作为targetBeanName 从spring容器中寻找,所以这个名字不能错;
    	     2、当然也可以手动使用initParam标签指定targetBeanName -->
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

AppConfig

@Configuration
@Import({MySecurityConfig.class})
public class AppConfig {

}

CustomizeDispatcherServlet

public class CustomizeDispatcherServlet extends DispatcherServlet {
    public Class<?> getContextClass() {
        return AnnotationConfigWebApplicationContext.class;
    }
}

Servlet3.0 配置(更精简,可忽略)

下面这样配置,更加精简,但是需要了解Servlet3.0 相关的规范,上面是传统xml配置,但是传统xml配置,好像也没人会这么去写,写了也会被打死,因为xml加上注解搞在一起,这不找打嘛。但是如果掌握了,能很好的过渡到全注解。如果这2个都会了的话,那理解springboot中的这些配置不就更简单了么

MyWebApplicationInitializer

public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{AppConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MyWebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
*/

SpringSecurityApplicationInitializer

// 目的是为了配置: DelegatingFilterProxy去包含springSecurityFilterChain的名字
public class SpringSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    public SpringSecurityApplicationInitializer() {
    }
}

MyWebConfig

@Configuration
@EnableWebMvc
// 这个注解为什么不能放到下面的MySecurityConfig中?
// 在springboot里面,其实是可以放到下面这个配置类中。但注意,这里是不行的。
// 这里有个父子容器的概念在里面,如果放在下面, 根容器中会有自动代理创建器创建出来,能够开启代理。
// 但是此时mvc的controller组件,还未被加载出来,也就不会被代理,也就达不到方法拦截的目的了,所以要放在这里
// 而springboot中可以,是因为springboot已经摒弃了父子容器的概念,全局就一个spring容器,所以就能够代理到controller
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan(basePackages = "com.zzhua.controller"
        ,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION
        ,classes = Service.class)})
public class MyWebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/login");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/out").setViewName("out");
    }
    
	@Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        // 开启静态资源访问,具体原理见:DefaultServletHandlerConfigurer一看就明白了
        configurer.enable();
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(responseBodyConverter());
    }

    @Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        return new StringHttpMessageConverter(Charset.forName("UTF-8"));
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/view/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
    
    // 支持自定义access
	@Bean
    public MyAccessServiceImpl myAccessServiceImpl() {
        return new MyAccessServiceImpl();
    }

}

MySecurityConfig

@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/img/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
       			.csrf().disable() // 关闭csrf,否则它会把请求给拦了,
       			                  // 不交给下一个拦截器处理,
       			                  // 请求就交给tomcat然后结束了
                .authorizeRequests()
                .antMatchers("/r/r1").hasAuthority("p1")
                .antMatchers("/r/r2").hasAuthority("p2")
                .antMatchers("/r/r3").hasRole("admin")
                .antMatchers("/**").authenticated()
                // .anyRequest().permitAll()
                
                // 自定义access, 
                // request和authentication这2个属性,可见 WebSecurityExpressionRoot 
                //            
               .anyRequest().access("@myAccessServiceImpl.myUri(request,authentication)")
                .and()
                .formLogin() 
                    // 如果设置了login相关的url,则security不会配置默认的登录页面了,具体可查看FormLoginConfigurer#initDefaultLoginFilter方法中的配置
                    // .successForwardUrl("/login-success") // 登录成功之后,转发到此url
                    .successHandler(
                            // 主要是想登录成功之后修改掉浏览器地址栏的url,所以改为重定向而非转发
                            (request, response, authentication) ->
                                    response.sendRedirect("/login-success"))
                    .loginProcessingUrl("/doLogin")
                    .loginPage("/login")
                    .permitAll()
                .and()
                .logout()
                    .logoutUrl("/doLogout")
                    .logoutSuccessUrl("/login")
        ;
		http
                .sessionManagement()
                .maximumSessions(1)  // 每个用户最大会话数
                .maxSessionsPreventsLogin(true) // 当超过最大会话数时,不能再登录了
    }

 // 配置用户信息

    @Bean // 一定要加@Bean,
    // 这个bean将会被AuthenticationConfiguration的getAuthenticationManager方法中,
    // 遍历的所有GlobalAuthenticationConfigurerAdapter实现中的
    // InitializeUserDetailsBeanManagerConfigurer$InitializeUserDetailsManagerConfigurer的configure方法,从spring容器中查找UserDetailsService类型的bean
    public UserDetailsService myUserDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // 角色和权限的区别就是:角色前面多了个ROLE_
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1","ROLE_admin").build());
        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
        return manager;
        // return new CustomizeUserDetailsService();
    }

 // 密码匹配器

    @Bean
    public PasswordEncoder myPasswordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }


}

自定义access

MyAccessService

public interface MyAccessService {
    public boolean myUri(HttpServletRequest request, Authentication authentication);
}

MyAccessServiceImpl

public class MyAccessServiceImpl implements MyAccessService {
    @Override
    public boolean myUri(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if(principal instanceof UserDetails){
            UserDetails userDetails = (UserDetails) principal;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            boolean contains = authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
            return contains;
        }

        return false;
    }
}

IndexController

@Controller
public class IndexController {

    @RequestMapping("index")
    public String index() {
        return "index";
    }

    @RequestMapping("login-success")
    public String loginSuccess() {
        return "login_success";
    }
}

RController

@RestController
@RequestMapping("r")
public class RController {
    /**
     * 测试资源1
     *
     * @return
     */
    @GetMapping(value = "r1")
    public String r1() {
        return " 访问资源1";
    }

    /**
     * 测试资源2
     *
     * @return
     */
    @GetMapping(value = "r2")
    public String r2() {
        return " 访问资源2";
    }

    @GetMapping(value = "r3")
    public String r3() {
        return " 访问资源3";
    }

    @RequestMapping("r4")
    @PreAuthorize("hasAnyRole('admin')")
    public String r4() {
        return "访问资源4";
    }
}

jsp页面

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
 halo~
</body>
</html>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form method="post" action="/doLogin">
        用户名:<input type="text" name="username">
        密码:  <input type="text" name="password">
        <input type="submit" value="提交">
    </form>
</body>
</html>

login_success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    登录成功
</body>
</html>

out.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    退出成功
</body>
</html>

密码匹配器

package com.zzhua.service;

import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class TestA {

    public static void main(String[] args) {

        /**
         $2a$10$oAs0HT5f8Trw1FOib6G9rO
         $2a$10$oAs0HT5f8Trw1FOib6G9rOMAx9LVoDZvYKESW36wo6cwZMvFRaPu2
         
         $2a$10$oAs0HT5f8Trw1FOib6G9rOMAx9LVoDZvYKESW36wo6cwZMvFRaPu2
         $2a$10$81sTtfl7YuogO6gtmhh2lePmgUVEzlOxprVNe.DlJDF0gYK5aSUCS
         
         $2a$10$ZUnxWS9swsf6tXiDJCwIP.LKlUbtxz8beAuXX3rRhqtUCj7x2C1XS
         $2a$10$0K7cxz7LsxEsB5XTXwD2re9SOq0uzmIbA8nZKknfvqNzSoF6yZTh2
         $2a$10$MheMDrGAJrRH5qOGk66ZvOmOx48MCHneMBKnhEVKDUIYijHalUYWS
         */

        String salt = BCrypt.gensalt();
        System.out.println(salt);
        String encodedPwd1 = BCrypt.hashpw("123456", salt);
        String encodedPwd2 = BCrypt.hashpw("123456", salt);
        System.out.println(encodedPwd1);
        System.out.println(encodedPwd2);

        String encodedPwd3 = BCrypt.hashpw("123456", BCrypt.gensalt());
        String encodedPwd4 = BCrypt.hashpw("123456", BCrypt.gensalt());
        System.out.println(encodedPwd3);
        System.out.println(encodedPwd4);

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        System.out.println(encoder.encode("123456"));
        System.out.println(encoder.encode("123456"));
        encoder.matches(encodedPwd1, "123456");
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值