原文链接:http://www.jylt.cc/#/detail?id=dbfcb0d72b016dc3e52fcab29a101845
框架简介
spring security 是一款基于Spring 框架的框架,提供了一套web应用安全性的完整解决方案。
web应用的安全性一般分为:用户认证和用户授权两部分。
用户认证就是用来区分当前访问的用户、设备等身份。比如我们常用的登录操作就属于用户认证,登录之后系统便知道我们是谁。
用户授权就是辨识当前用户拥有的角色、权限。比如用户是不是管理员角色,如果是管理员角色就可以增加、删除、修改普通用户信息,这些增加、删除、修改就是用户所拥有的权限。
用户授权和用户授权如果我们自己开发,会花费较多的经历来处理各种逻辑。而spring security就可以大大简化我们的这些操作,提高开发效率,并且还能减少一些不经意的bug。
spring boot集成 spring security
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.formLogin()
.usernameParameter("account")
.passwordParameter("password")
.loginProcessingUrl("/user/login")
// .successForwardUrl()
.successHandler(new MySuccessHandler())
// .failureForwardUrl()
.failureHandler(new MyFailHandler());
httpSecurity.authorizeRequests()
.antMatchers("/content/list_contents", "/user/sendValidCode",
"/user/add_user").permitAll()
.regexMatchers().permitAll()
.anyRequest().authenticated();
// 异常处理
httpSecurity.exceptionHandling()
// 权限异常处理
.accessDeniedHandler(new MyAccessDeniedHandler());
httpSecurity.csrf().disable();
}
}
参数说明
- httpSecurity.formLogin():登录数据处理
方法名 | 说明 |
---|---|
usernameParameter | 自定义接收前端用户名字段,spring security框架默认只接收username 字段,其他字段接收为"" |
passwordParameter | 自定义接收前端密码字段,spring security框架默认只接收password 字段,其他字段接收为"" |
loginProcessingUrl | 登录接口url |
successForwardUrl | 登录成功后跳转页面的适配器,前后端分离的时候可以使用,参考类:org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler |
successHandler | 登录成功后跳转页面的适配器,前后端分离的时候可以使用,参考类:org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler |
failureForwardUrl | 登录失败后跳转的页面,前后端不分离的时候可以使用 |
failureHandler | 登录失败后跳转页面的适配器,前后端分离的时候可以使用,参考类:org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler |
说明:usernameParameter、passwordParameter可参考类:org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
- httpSecurity.authorizeRequests():授权
方法名 | 说明 |
---|---|
antMatchers().permitAll | 忽略的资源路径,这里的资源直接被框架放行,比如说:注册接口、一些静态资源文件等不需要登录就可以访问 |
regexMatchers().permitAll | 使用正则表达式忽略指定资源路径 |
anyRequest().authenticated() | 任意请求都需要是已经认证过的,即只有登录后才可以访问的接口 |
注意:
antMatchers
必须放在anyRequest
之前,否则会报异常:Can't configure antMatchers after anyRequest
说明:
antMatchers
参数是不定参数,每个参数都是一个ant
表达式,用于匹配url规则。规则如下:
?:匹配一个字符
方法名 | 说明 |
---|---|
? | 匹配一个字符 |
* | 匹配0或多个字符 |
** | 匹配0或多个目录 |
MySuccessHandler.class 登陆成功处理器
public class MySuccessHandler implements AuthenticationSuccessHandler {
public MySuccessHandler() {
}
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
PrintWriter writer = httpServletResponse.getWriter();
ResponseKit<String> res = ResponseKit.success("登录成功");
writer.write(JSONUtil.toJsonStr(res));
writer.flush();
writer.close();
}
}
MyFailHandler.class 登陆失败处理器
public class MyFailHandler implements AuthenticationFailureHandler {
public MyFailHandler() {
}
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
PrintWriter writer = httpServletResponse.getWriter();
ResponseKit<String> res = ResponseKit.error(HttpStatus.FORBIDDEN.value(), "用户名或密码错误");
writer.write(JSONUtil.toJsonStr(res));
writer.flush();
writer.close();
}
}
MyAccessDeniedHandler.class 权限异常处理器
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
PrintWriter writer = httpServletResponse.getWriter();
ResponseKit<Object> responseKit = ResponseKit.error(HttpStatus.FORBIDDEN.value(), "权限不足");
writer.write(JSONUtil.toJsonStr(responseKit));
writer.flush();
writer.close();
}
}
内置访问控制说明
上面授权的方法中有如:permitAll
允许所有,authenticated
需要授权的这些方法,以下是方法总结
方法名 | 说明 |
---|---|
permitAll | 允许所有资源访问 |
denyAll | 禁止所有资源访问 |
anonymous | 允许匿名访问的资源 |
authenticated | 只允许认证后可访问的资源 |
remember | 自动登录后能访问的资源 |
官方说明:
Expression | Description |
---|---|
hasRole(String role) | Returns true if the current principal has the specified role. For example, hasRole(‘admin’) By default if the supplied role does not start with ‘ROLE_’ it will be added. This can be customized by modifying the defaultRolePrefix on efaultWebSecurityExpressionHandler. |
hasAnyRole(String… roles) | Returns true if the current principal has any of the supplied roles (given as a comma-separated list of strings). For example, hasAnyRole(‘admin’, ‘user’) By default if the supplied role does not start with ‘ROLE_’ it will be added. This can be customized by modifying the defaultRolePrefix on DefaultWebSecurityExpressionHandler. |
hasAuthority(String authority) | Returns true if the current principal has the specified authority. For example, hasAuthority(‘read’) |
hasAnyAuthority(String… authorities) | Returns true if the current principal has any of the supplied authorities (given as a comma-separated list of strings) For example, hasAnyAuthority(‘read’, ‘write’) |
principal | Allows direct access to the principal object representing the current user |
authentication | Allows direct access to the current Authentication object obtained from the SecurityContext |
permitAll | Always evaluates to true |
denyAll | Always evaluates to false |
isAnonymous() | Returns true if the current principal is an anonymous user |
isRememberMe() | Returns true if the current principal is a remember-me user |
isAuthenticated() | Returns true if the user is not anonymous |
isFullyAuthenticated() | Returns true if the user is not an anonymous or a remember-me user |
hasPermission(Object target, Object permission) | Returns true if the user has access to the provided target for the given permission. For example, hasPermission(domainObject, ‘read’) |
hasPermission(Object targetId, String targetType, Object permission) | Returns true if the user has access to the provided target for the given permission. For example, hasPermission(1, ‘com.example.domain.Message’, ‘read’) |
角色权限控制
基于代码控制资源访问
基于权限进行访问
在配置spring security
的时候,可以对指定资源加上特定的访问权限,只有用户拥有该权限时才可以访问该资源。例如:
httpSecurity.authorizeRequests()
.antMatchers("/content/list_contents", "/user/sendValidCode",
"/user/add_user").permitAll()
.regexMatchers().permitAll()
.antMatchers("/edit").hasAnyAuthority("edit,edit_or_update")
.antMatchers("/update").hasAuthority("edit_or_update")
.anyRequest().authenticated();
说明:上面代码中的
.antMatchers("/edit").hasAnyAuthority("edit,edit_or_update")
,表示只有拥有edit
或者edit_or_update
权限的用户才可以访问/edit
资源;其中hasAnyAuthority()
表示只要拥有任一权限都可访问前面定义的资源;
.antMatchers("/update").hasAuthority("edit_or_update")
说明只有拥有edit_or_update
权限的用户才可以访问/update
资源;其中hasAuthority()
表示只用该权限的才可以访问前面定义的资源。
基于角色进行访问
在配置spring security
的时候,可以对指定资源加上特定的访问角色,只有用户拥有该角色时才可以访问该资源。例如:
httpSecurity.authorizeRequests()
.antMatchers("/content/list_contents", "/user/sendValidCode",
"/user/add_user").permitAll()
.regexMatchers().permitAll()
.antMatchers("/edit").hasAnyRole("admin,user")
.antMatchers("/update").hasRole("admin")
.anyRequest().authenticated();
说明,
haseRole()
和hasAnyRole()
使用跟上面hasAnyAuthority()
和hasAuthority
类似。
注意: 角色不能以ROLE_
开头。
基于IP地址进行访问
在工作中有时候可能只允许特定IP访问服务,这个时候可以根据IP地址进行授权。
httpSecurity.authorizeRequests()
.antMatchers("/content/list_contents", "/user/sendValidCode",
"/user/add_user").permitAll()
.regexMatchers().permitAll()
.antMatchers("/edit").hasIpAddress("127.0.0.1")
.anyRequest().authenticated();
其中
hasIpAddress()
就是允许访问的IP地址。
基于注解控制资源访问
开启注解
使用注解之前需要先开启注解控制功能,在启动类上打开注解功能:@EnableGlobalMethodSecurity(prePostEnabled = true)
使用
@PreAuthorize(String val)
:该注解用于方法或类上,在类或方法执行之前先判断是否拥有指定角色或者权限,其中参数与上面说到的基于代码控制资源访问内容相似,可以有以下写法:
- @PreAuthorize(“hasRole(value)”):拥有value角色才可访问
- @PreAuthorize(“hasAnyRole(admin,user)”):拥有admin、user任一角色都可访问
@PreAuthorize("hasRole(admin)")
@PostMapping("/save")
public void saveO() {
}