Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。根据自己的需要,可以使用适当的过滤器来保护自己的应用程序。
注:这篇不牵扯原理及大量理论知识,只是一个入门案例,但是完全足够大家理解和写简单的项目。
因为这一篇是讲spring-security 就是用spring写的 但是我在这篇文章中 并没有使用xml配置
全文都是使用javaconfig 进行配置的。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
</dependencies>
@Configuration
@ComponentScan(basePackages = "com.itheima.security.springmvc"
,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value =
Controller.class)})
public class ApplicationConfig {
//在此配置除了Controller的其它bean,比如:数据库链接池、事务管理器、业务bean等。
}
在config包下定义WebConfig.java,它对应s对应于DispatcherServlet配 置
@Configuration // 相当于springmvc 文件
@EnableWebMvc
@ComponentScan(
basePackages = "com.wyh",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {
// 视图解析器
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
// 把根路径跳转到login上
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login");
}
}
在init包下定义Spring容器初始化类SpringApplicationInitializer,此类实现WebApplicationInitializer接口, Spring容器启动时加载WebApplicationInitializer接口的所有实现类 加载 WebSecurityConfig
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// spring 容器
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ ApplicationConfig.class , WebSecurityConfig.class};
}
//servletContext
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
//url-mapping
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
Spring Security初始化,这里有两种情况
若当前环境没有使用Spring或Spring MVC,则需要将 WebSecurityConfig(Spring Security配置类) 传入超 类,以确保获取配置,并创建spring context。
相反,若当前环境已经使用spring,我们应该在现有的springContext中注册Spring Security(上一步已经做将 WebSecurityConfig加载至rootcontext),此方法可以什么都不做。 在init包下定义SpringSecurityApplicationInitializer:
public class SpringSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
public SpringSecurityApplicationInitializer(){
}
}
默认根路径请求 在WebConfig.java中添加默认请求根路径跳转到/login,此url为spring security提供:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login");
// registry.addViewController("/login-view").setViewName("login");
}
因为这次只是写一个入门案例 页面就没有额外写了 但是怎么设置还是有说滴。
默认认证页面
8.1.1、 在config包WebConfig.java中
//默认Url根路径跳转到/login,此url为spring security提供
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login‐view");
registry.addViewController("/login‐view").setViewName("login");
}
8.1.2、在WebSecurityConfig.java中 增加配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // 关闭csrf
.authorizeRequests()
.antMatchers("/r/r1").hasAnyAuthority("p1")// 表示:访问/r/r1资源的 url需要拥有p1权限。
.antMatchers("/r/r2").hasAnyAuthority("p2")
.antMatchers("/r/**").authenticated() // 访问/r/下所有请求都需要通过身份认证
.anyRequest().permitAll() //指定任何人都允许使用URL
.and()
.formLogin() // 支持form表单认证,认证成功后转向/login-success。
.loginPage("/login‐view") //1
.loginProcessingUrl("/login")// 2
.successForwardUrl("/login-success") // 自定义登录成功的配置 认证通过后跳转到login-success
.permitAll(); //3
}
1、指定我们自己的登录页,spring security以重定向方式跳转到/login-view
2、指定登录处理的URL,也就是用户名、密码表单提交的目的路径
3、我们必须允许所有用户访问我们的登录页(例如为验证的用户),这个 formLogin().permitAll() 方法允许 任意用户访问基于表单登录的所有的URL。
spring security提供了用户名密码登录、退出、会话管理等认证功能,只需要配置即可使用。
在config包下定义WebSecurityConfig,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean //密码编码器、
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override //安全拦截机制。
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/r/r1").hasAnyAuthority("p1") // 访问/r/r1 需要有p1权限 下同
.antMatchers("/r/r2").hasAnyAuthority("p2")
.antMatchers("/r/**").authenticated() // 访问/r/下所有请求都需要通过身份认证
.anyRequest().permitAll() //指定任何人都允许使用URL
.and()
.formLogin() // 支持form表单认证,认证成功后转向/login-success。
.successForwardUrl("/login-success"); // 自定义登录成功的配置
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyUser{
private Integer id;
private String username;
private String password;
}
在MyUserDetailsService()方法中,我们返回了一个UserDetailsService给spring容器,Spring Security会使用它来 获取用户信息
今天是初使用 不写多了…。
@Component
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 登录账号
// 这里是模拟数据库查询
MyUser myUser = getUserByName(username);
if (myUser== null) {
return null;
}
//创建userDetails
UserDetails userDetails =
User.withUsername(myUser.getUsername()).password(BCrypt.hashpw(myUser.getPassword(),BCrypt.gensalt())).authorities("p1").build();
// pssword() 使用了密码加密 加密方式在之前讲了。
// 数据库里的密码是不会存明文密码 这里也是在模拟。
// 因为我使用了密码加密 我登录输入的密码
// security 在处理的时候 也会进行加密 然后再比对。
return userDetails;
}
private MyUser getUserByName(String username){
return new MyUser(1,username,"123456");
}
}
测试的请求
@RestController
public class LoginController {
@RequestMapping(value = "/login-success", produces = "text/plain;charset=utf-8")
public String loginSuccess(){
return getUsername()+"登录成功";
}
@RequestMapping(value = "/r/r1", produces = "text/plain;charset=utf-8")
public String r1(){
return getUsername()+"资源1";
}
@RequestMapping(value = "/r/r2", produces = "text/plain;charset=utf-8")
public String r2(){
return getUsername()+"资源2";
}
// 这个权限是 身份验证通过就可以访问的 不要用户有权限的。
@RequestMapping(value = "/r/r3", produces = "text/plain;charset=utf-8")
public String r3(){
return getUsername()+"这个用户是没有任何权限 这个请求也只要登录验证就可"+ " 资源3";
}
private String getUsername(){
String username=null;
//当前通过的用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if (principal==null){
username="匿名";
}
if(principal instanceof UserDetails){
UserDetails userDetails= (UserDetails)principal;
username = userDetails.getUsername();
}else{
username= principal.toString();
}
return username;
}
}
当输入错误的密码的时候 会自动弹出提示。
当用户名和密码都输入正确的时候 它就会跳转到 /login-success请求去
当我们的用户有权限的时候 就是用我现在模拟的数据(username:admin password:123456 p1 权限)
来访问 /r/r1
接下来接着访问 /r/r2 /r/r2 是需要p2权限 模拟的数据是没有这个权限
会报 403 错误 权限不足。
在 /r/** 下 所有用户都需要身份验证通过才可以访问的。
如果在没有登录的情况下 访问 /r/r3 是会自动转到登录页面去。
今天是做了一个security 的入门案例 。
今天比平时写的时间还长一些。
看完的话 最基本的是可以用了 那里面的数据 是可以从数据库里面查询的。
权限表的设计 在我之前一篇博客已经发出来了。
等过几天把springboot-security 的案例也写出来
那个是从数据库查询的,不再是模拟数据拉。
继续。