springsecurity
搭建
我们在springmvc
项目的基础上,集成springsecurity
引入必要的jar
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
继承AbstractSecurityWebApplicationInitializer
类
该类继承了WebApplicationInitializer
,所以会被spring容器发现,并且spring会用它在web容器中注册DelegatingFilterProxy
,而这个类会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain
的bean。
springSecurityFilterChain
本身是一个特殊的Filter,它可以链接任意一个或多个其他的filter.Spring security
依赖一系列Servlet Filter
来提供不同的安全特性。
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
让Web安全性运行起来
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
@EnableWebSecurity
注解会启用Web安全功能。但它本身并没有什么作用,它需要配置在一个实现了WebSecurityConfigurerAdapter
类的配置类尚
让spring扫描到该配置类
public class WebApplication extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class, SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
不然会报找不到springSecurityFilterChain
这个bean
到这里我们还无法访问程序,因为 1没有配用户的相关信息 2 没有配访问路径权限控制
spring支持内存、关系数据库和LDAP
。
基于内存的用户存储访问
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()// 内存存储用户
.withUser("zhangsan")// 创建一个用户名
.password("123")//密码
.roles("guest")//赋予的角色
.and()// 连接符
.withUser("admin")
.password("123")
.roles("admin");
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启登陆配置
.antMatchers(HttpMethod.GET,"/swagger").permitAll()// 不用登陆的路径
.anyRequest().authenticated()// 其他路径都要认证授权
.and().formLogin()// 支持form表单登陆
.and().httpBasic();// 支持basic登陆
}
}
只需要重写configure(AuthenticationManagerBuilder auth)
方法,就可以配置多种用户存储方式。
重写configure(final HttpSecurity http)
就可以配置路径访问控制。
新建一个控制器
@Controller
public class LoginController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home() {
System.out.println("home");
return "home";
}
@RequestMapping(value = "user", method = RequestMethod.GET)
public String user() {
System.out.println("user");
return "user";
}
}
补充:如果我们直接访问 /user,那么会被拦截进行登陆验证,然后输入用户名密码后,并没有重定向到/user,而是重定向到"/"路径,这点要注意。当然我们也可以配置登陆成功的路径。
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启登陆配置
.antMatchers(HttpMethod.GET, "/swagger").permitAll()
.anyRequest().permitAll()
.and().formLogin()
.defaultSuccessUrl("/success")// 登陆成功后默认的跳转的URL
.and().httpBasic();
}
另外,/login这个路径是springsecurity
的默认登陆路径,如果我们直接访问该路径,则会跳转到登陆页面。
使用关系型数据库
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService uds;
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
// 配置数据库访问方式
auth.userDetailsService(uds);
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启登陆配置
.antMatchers(HttpMethod.GET, "/swagger").permitAll().anyRequest().authenticated().and().formLogin()
.and().httpBasic();
}
}
@Component
public class UserService implements UserDetailsService {
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
if (!"zhangsan".equals(username)) {
// 如果数据库不存在,则抛UsernameNotFoundException,springsecurity会捕获该异常
throw new UsernameNotFoundException("not zhangsan");
}
// 获取用户权限
List<GrantedAuthority> authoritys = new ArrayList<GrantedAuthority>();
authoritys.add(new SimpleGrantedAuthority("admin"));
return new User("zhangsan", "123", authoritys);
}
}
退出
退出功能是通过Servlet
容器中的filter实现的。这个Filter会拦截针对“/logout"的请求,因此只需要调用”/logout“即可。
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启登陆配置
.antMatchers(HttpMethod.GET, "/swagger").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.and().httpBasic()
.and().logout()// 配置退出
.logoutSuccessUrl("/");//退出成功重定向的url
}
但是上面配置后,调用”/logout“没生效,而且报404
原来:默认情况下”/logout”必须使用POST提交才可以起作用
原因:CSRS
功能默认开启了
解决办法:要么使用post请求,要么关闭CSRS
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启登陆配置
.antMatchers(HttpMethod.GET, "/swagger").permitAll()
.anyRequest().authenticated().and().formLogin() .and().httpBasic()
.and().logout().logoutSuccessUrl("/")
.and().csrf().disable();// 关闭CSRS
}