一、jar包依赖
<!--对应的版本-->
<spring.version>4.3.22.RELEASE</spring.version>
<security.version>4.2.11.RELEASE</security.version>
<!--spring security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${security.version}</version>
</dependency>
二、基本配置
- 通过使用
@EnableWebSecurity
注解开启配置支持 - 继承
WebSecurityConfigurerAdapter
类,进行各种配置
1、用户储存认证配置
在spring security中提供四种认证方式
- 基于内存的用户存储
该种方式适合单一用户,个人项目使用,具体操作如下
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("lkl").password("lkl").roles("ADMIN")
.and()
.withUser("admin").password("admin").authorities("ROLE_ADMIN");
}
- 数据库表用户存储认证
该方式在一定程度上使用数据库,较上一方式用户可以动态更新
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(datasource)
// 根据用户名查询用户语句
.usersByUsernameQuery("select account,password from user where account=?")
// 根据用户名查询用户认证权限语句
.authoritiesByUsernameQuery("select account,role,true from user where account=?");
}
- 配置自定义的用户存储认证
该方式通过向认证器提供一个认证service实现,认证service需实现
UserDetailsService
接口,该方法最为灵活,推荐使用
// 实现接口,提供认证service
@Service
public class SecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ADMIN"));
return new User("admin", "admin", authorities);
}
}
// 仅需一句话即可完成配置
@Autowired
private SecurityService securityService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(securityService);
}
- 基于LDAP进行用户存储认证
不会
二、密码加密策略
共提供了九种加密方式和一种自定义接口,简单列举几个如下
-
BCryptPasswordEncoder
强hash方式加密,推荐使用 Pbkdf2PasswordEncoder
SCryptPasswordEncoder
- 实现
org.springframework.security.crypto.password.PasswordEncoder
接口 自定义加密方式
除此外还提供了
PasswordEncoderFactories.createDelegatingPasswordEncoder()
以委托的方式创建加密策略。使用比较简单,只需要在用户储存配置后在配置一下即可。
auth.userDetailsService(securityService)
.passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
三、请求拦截策略
- 编码式配置权限拦截
请求拦截通过重载configure(HttpSecurity)方法实现,支持两种匹配风格,
ant
和正则。
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// .antMatchers("/user/**").anonymous() 匿名访问
.antMatchers("/user/**").hasAuthority("ADMIN");
// .regexMatchers("").anonymous(); // 正则方式
http.requiresChannel().antMatchers("/admin/info")
// .requiresInsecure() 强制http请求
.requiresSecure(); // 强制https请求
}
请求保护方法一览
access(String) 如果给定的SpEL表达式计算结果为true,就允许访问
anonymous() 允许匿名用户访问
authenticated() 允许认证过的用户访问
denyAll() 无条件拒绝所有访问
permitAll() 无条件允许访问
fullyAuthenticated() 如果用户是完整认证的话(不是通过Remember-me功能认证的),就允许访问
hasAnyAuthority(String...) 如果用户具备给定权限中的某一个的话,就允许访问
hasAnyRole(String...) 如果用户具备给定角色中的某一个的话,就允许访问
hasAuthority(String) 如果用户具备给定权限的话,就允许访问
hasIpAddress(String) 如果请求来自给定IP地址的话,就允许访问
hasRole(String) 如果用户具备给定角色的话,就允许访问
not() 对其他访问方法的结果求反
rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问
requiresChannel() // 请求通道配置
- 声明式权限过滤
共提供了三组注解来实现声明式配置
-
@PreAuthorize
前置访问授权控制,支持Spring-EL表达式,如:@PreAuthorize("hasAuthority('ROLE_ADMIN')")
推荐长期使用。不需要过多介绍,自动提示做的很好。 -
@PostAuthorize
后置访问授权控制,可以拿到返回的结果returnObject
,使用的不多 -
@Secured
因不支持spring自家的Spring-EL而遭到嫌弃,效果也还是可以达到的 -
JSR-250
详情查看jsr-250标准
==注:在我看的文档中说到要使用注解需要先显示的声明启用注解@EnableGlobalMethodSecurity(prePostEnabled = true)
,测试中发现并不需要,使用版本为4.2.11.RELEASE
,文档版本3.2.2.RELEASE
。==
四、自定义登录登出
研究半天才发现,security它尽然是自带登录处理的,一直在想shiro中使用SecurityUtils.login来登录,这用什么呢?没想到啥都不用,默认提供好了。当然咯默认的都是很简单的还是需要自定义才好。默认的登录也还是需要显示启用下的,简单一句配置就行。
// http请求拦截中声明
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll() // 重点一句
自定义也很简单
.formLogin().loginPage("/mylogin").permitAll()
需要自己写个mylogin页面
@RequestMapping("/mylogin")
public String login() {
return "/login";
}
// 自定义登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>security登录</title>
</head>
<body>
<form method="post" action="/mylogin">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<input type="submit" value="登录">
</form>
</body>
</html>
==注:表单的提交地址要和自定义的登录地址一样,springsecurity会自动帮我们处理。==
自定义登出稍微复杂一点,需要在控制器中写不少代码了~
// 配置一下自定义的登出url
.logout().logoutUrl("/logout").permitAll();
提供一个登出控制器,代码如下
@RequestMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
logoutHandler.logout(request, response, auth);
}
return "redirect:/login?logout";
}
五、让Security记住我
记住我是一个比较通用的功能,一般记住我都是使用cookie来实现的,
spring security
当然也不例外,接下来就实现记住我功能。
- 简单的配置实现记住我
@Override
protected void configure(HttpSecurity http) throws Exception {
....省略其他配置
http.rememberMe().rememberMeCookieName("remember-me")
.rememberMeParameter("remember-me")
.userDetailsService(userDetailsService);
}
OK了,没其他配置了,就这样就行了,在提交表单时加一个remember-me=true
就可以了。
至此一个简单的springsecurity入门就结束了,更灵活的自定义在慢慢研究~~
源码
网友评论