目录:
(1)权限判断
(2)角色的判断
(3)通过IP地址判断
(4)自定义403处理方案
(5)acess方法使用
(6)access结合自定义方法实现权限控制
(1)权限判断
permitAll()这些都是用户登录之后的的权限访问(权限控制),这个只是判断用户是否登录,那么我们正常的工作中的,我们大部分网站都由会员系统,也就是说你登陆之后,的确能访问部分内容,但是它也会判断你的用户呢是否有会员的权限,如果你有会员的权限,它会给你更多的内容给你访问,没有会员权限,只有默认的功能给你访问,SpringSecurity也支持了角色的判断,这个方法用于用户已经被认证之后,判断用户是否特定的要求,会员的权限,对应的方法:
这个权限是在我们自定义逻辑中,创建的User对象的时候,指定的 这里我们给了User权限:admin、normall
这里我们判断当登录之后会跳到main.html,给它加一个跳转让他跳到另一个html,我们对另外一个html做一些权限的控制,我们规定好拥有admin这个权限,才能跳转到这个页面:
main.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功!!!!<a href="/main1.html">跳转</a>
</body>
</html>
main1.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
main1.html
</body>
</html>
更改配置类SecurityConfig:添加权限判断,使用控制类,successForwardUrl跳转页面,注释处理器
package com.xxxx.springsecuritydemo.config;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationFailureHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationSucessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//自定义登录页面,不使用自带的
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//和Login中input的属性相对应
.usernameParameter("username1")
.passwordParameter("password1")
//当发现是 /login是认为是登录,必须和表单提交的地址一样,去执行UserDetailsServiceImpl登录逻辑
.loginProcessingUrl("/login")
//自定义登录页面
.loginPage("/login.html")
//登录成功之后跳转页面,必须是Post请求
.successForwardUrl("/toMain")
//登录成功后的处理器,不能和sucessForwardUrl共存
//.successHandler(new MyAuthenticationSucessHandler("http://wwww.baidu.com"))
//登录失败跳转页面,Post请求
.failureForwardUrl("/toError");
//登录失败后的处理器,不能和failureForwardUrl共存
//.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
//授权认证 相当于拦截器一样
http.authorizeRequests()
//error.html不需要被认证
.antMatchers("/error.html").permitAll()
//login.html不需要被认证
.antMatchers("/login.html").permitAll()
//resources目录下的静态资源
.antMatchers("/static/js/**", "/static/css/**", "/static/images/**").permitAll()
//regexMatchers放行的使用
//.regexMatchers(".+[.]png").permitAll()
//.regexMatchers(HttpMethod.GET,"/demo").permitAll()
//.mvcMatchers("/demo").servletPath("/xxxx").permitAll()
//如果不习惯mvcMatchers还可以用antMatchers设置等效
.antMatchers("/xxxx/demo").permitAll()
//任何目录下的png图片
//.antMatchers("/**/*.png").permitAll()
//判断用户是否有admin权限
//.antMatchers("/main1.html").hasAuthority("admin")
//可以设置这个页面,多个权限去访问
.antMatchers("/main1.html").hasAnyAuthority("admin","normall")
//所有请求都必须被认证,必须登录之后能被访问
.anyRequest().authenticated();
//关闭csrf防护
http.csrf().disable();
}
//创建PasswordEncoder实例
@Bean
public PasswordEncoder getPw(){
return new BCryptPasswordEncoder();
}
}
因为登录的用户有admin权限,如果把权限判断条件进行更改,就会出现:权限不足的403
(2)角色的判断
对用户具备某些角色就进行访问,否则就403,角色也就是在自定义登录逻辑的时候就有的,定义角色必须以ROLE_开头,固定
package com.xxxx.springsecuritydemo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder pw;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1.查询数据库判断用户名是否存在,如果不存在就会抛出UsernameNotFoundException异常
//这里进行模拟,用户名为admin
if (!"admin".equals(username)) {
throw new UsernameNotFoundException("用户名不存在");
}
//2.//如果存在,把查询出来的密码(注册时已经经过加密) 进行解析,或者直接把密码放到构造方法
String password=pw.encode("123");
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normall,ROLE_abc"));
}
}
注释掉权限判断,使用角色判断,注意前面定义的角色,这里不带ROLE_ ,只使用abc就行了,严格区分大小写
(3)通过IP地址判断
我们项目里面有一个后台管理系统,而这个后台管理系统的只能根据我们指定的一台服务器上面去进行登录,去进行一些响应的操作,这台服务器的IP地址是固定的,那我们呢可以通过IP地址进行权限的控制,如果不是这个IP地址,我们不允许你访问
MyAuthenticationSucessHandler:
package com.xxxx.springsecuritydemo.handle;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
private String url;
public MyAuthenticationSucessHandler(String url) {
this.url = url;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//打印IP地址
System.out.println(request.getRemoteAddr());
//其中的一个方法获取User对象
User user = (User)authentication.getPrincipal();
System.out.println(user.getUsername());
System.out.println(user.getPassword());//null
System.out.println(user.getAuthorities());//权限
response.sendRedirect(url);
}
}
使用localhost:
使用127.0.0.1:
使用本机IP地址:
package com.xxxx.springsecuritydemo.config;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationFailureHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationSucessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//自定义登录页面,不使用自带的
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//和Login中input的属性相对应
.usernameParameter("username1")
.passwordParameter("password1")
//当发现是 /login是认为是登录,必须和表单提交的地址一样,去执行UserDetailsServiceImpl登录逻辑
.loginProcessingUrl("/login")
//自定义登录页面
.loginPage("/login.html")
//登录成功之后跳转页面,必须是Post请求
//.successForwardUrl("/toMain")
//登录成功后的处理器,不能和sucessForwardUrl共存
.successHandler(new MyAuthenticationSucessHandler("/main.html"))
//登录失败跳转页面,Post请求
.failureForwardUrl("/toError");
//登录失败后的处理器,不能和failureForwardUrl共存
//.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
//授权认证 相当于拦截器一样
http.authorizeRequests()
//error.html不需要被认证
.antMatchers("/error.html").permitAll()
//login.html不需要被认证
.antMatchers("/login.html").permitAll()
//resources目录下的静态资源
.antMatchers("/static/js/**", "/static/css/**", "/static/images/**").permitAll()
//regexMatchers放行的使用
//.regexMatchers(".+[.]png").permitAll()
//.regexMatchers(HttpMethod.GET,"/demo").permitAll()
//.mvcMatchers("/demo").servletPath("/xxxx").permitAll()
//如果不习惯mvcMatchers还可以用antMatchers设置等效
.antMatchers("/xxxx/demo").permitAll()
//任何目录下的png图片
//.antMatchers("/**/*.png").permitAll()
//判断用户是否有admin权限
//.antMatchers("/main1.html").hasAuthority("admin")
//可以设置这个页面,多个权限去访问
//.antMatchers("/main1.html").hasAnyAuthority("admin","normall")
//角色判断
//.antMatchers("/main1.html").hasRole("abc")
//判断有多个角色权限
//.antMatchers("/main1.html").hasAnyRole("abc,abC")
//IP地址判断
.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
//所有请求都必须被认证,必须登录之后能被访问
.anyRequest().authenticated();
//关闭csrf防护
http.csrf().disable();
}
//创建PasswordEncoder实例
@Bean
public PasswordEncoder getPw(){
return new BCryptPasswordEncoder();
}
}
因为我们设置的是127.0.0.1:所以IP地址不正确
输入正确IP:127.0.0.1 成功跳转
这里我们演示的时候本机的IP地址是多变得,得到的IP地址是不一样的,但是在实际工作中呢,服务器那边的IP地址是固定的,不可能想本机一样测出多个,只会出现一个
(4)自定义403处理方案
我们在用SpringSecurity经常看到403无权限,这个页面在实际项目中呢对用户不是特别友好,用户看不懂这是什么东西,我们经常会处理一个页面,进行更加友好的展示,SpringSecurity也支持这种权限受限的页面展示,分为2步:
继承AccessDeniedHandler:
package com.xxxx.springsecuritydemo.handle;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
//设置403状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//响应头
response.setHeader("Content-Type","application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员\"}");
writer.flush();
writer.close();
}
}
配置类设置:先定义属性引入进来,再进行设置:
SecurityConfig:
package com.xxxx.springsecuritydemo.config;
import com.xxxx.springsecuritydemo.handle.MyAccessDeniedHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationFailureHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationSucessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//引用403处理这个类
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
//自定义登录页面,不使用自带的
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//和Login中input的属性相对应
.usernameParameter("username1")
.passwordParameter("password1")
//当发现是 /login是认为是登录,必须和表单提交的地址一样,去执行UserDetailsServiceImpl登录逻辑
.loginProcessingUrl("/login")
//自定义登录页面
.loginPage("/login.html")
//登录成功之后跳转页面,必须是Post请求
//.successForwardUrl("/toMain")
//登录成功后的处理器,不能和sucessForwardUrl共存
.successHandler(new MyAuthenticationSucessHandler("/main.html"))
//登录失败跳转页面,Post请求
.failureForwardUrl("/toError");
//登录失败后的处理器,不能和failureForwardUrl共存
//.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
//授权认证 相当于拦截器一样
http.authorizeRequests()
//error.html不需要被认证
.antMatchers("/error.html").permitAll()
//login.html不需要被认证
.antMatchers("/login.html").permitAll()
//resources目录下的静态资源
.antMatchers("/static/js/**", "/static/css/**", "/static/images/**").permitAll()
//regexMatchers放行的使用
//.regexMatchers(".+[.]png").permitAll()
//.regexMatchers(HttpMethod.GET,"/demo").permitAll()
//.mvcMatchers("/demo").servletPath("/xxxx").permitAll()
//如果不习惯mvcMatchers还可以用antMatchers设置等效
.antMatchers("/xxxx/demo").permitAll()
//任何目录下的png图片
//.antMatchers("/**/*.png").permitAll()
//判断用户是否有admin权限
//.antMatchers("/main1.html").hasAuthority("admin")
//可以设置这个页面,多个权限去访问
//.antMatchers("/main1.html").hasAnyAuthority("admin","normall")
//角色判断
//.antMatchers("/main1.html").hasRole("abc")
//判断有多个角色权限
//.antMatchers("/main1.html").hasAnyRole("abc,abC")
//IP地址判断
.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
//所有请求都必须被认证,必须登录之后能被访问
.anyRequest().authenticated();
//关闭csrf防护
http.csrf().disable();
//异常处理
http.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler);
}
//创建PasswordEncoder实例
@Bean
public PasswordEncoder getPw(){
return new BCryptPasswordEncoder();
}
}
这是设置了127.0.0.1的IP地址判断,用localhost访问会403
返回json的数据
以上只是我们简单实现了信息的处理,在真正的项目中呢会有更好的页面去展示
(5)acess方法使用
.permitAll和access(“permitAll”) 效果相同
(6)access结合自定义方法实现权限控制
access()支持使用自定义的表达式
做一个demo判断登录的用户是否有访问当前url的权限
MyService接口:
package com.xxxx.springsecuritydemo.service;
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
public interface MyService {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
实现类:
package com.xxxx.springsecuritydemo.service;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
@Service
public class MyServiceImpl implements MyService {
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
//获取主体,这个主题就是User对象,它是UserDetail下面的
Object obj = authentication.getPrincipal();
//if判断是否属于UserDetail下面的
if (obj instanceof UserDetails){
UserDetails userDetails=(UserDetails) obj;
//获取权限
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
//判断是否含有uri
return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
}
return false;
}
}
配置类:
package com.xxxx.springsecuritydemo.config;
import com.xxxx.springsecuritydemo.handle.MyAccessDeniedHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationFailureHandler;
import com.xxxx.springsecuritydemo.handle.MyAuthenticationSucessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//引用403处理这个类
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
//自定义登录页面,不使用自带的
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//和Login中input的属性相对应
.usernameParameter("username1")
.passwordParameter("password1")
//当发现是 /login是认为是登录,必须和表单提交的地址一样,去执行UserDetailsServiceImpl登录逻辑
.loginProcessingUrl("/login")
//自定义登录页面
.loginPage("/login.html")
//登录成功之后跳转页面,必须是Post请求
//.successForwardUrl("/toMain")
//登录成功后的处理器,不能和sucessForwardUrl共存
.successHandler(new MyAuthenticationSucessHandler("/main.html"))
//登录失败跳转页面,Post请求
.failureForwardUrl("/toError");
//登录失败后的处理器,不能和failureForwardUrl共存
//.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
//授权认证 相当于拦截器一样
http.authorizeRequests()
//error.html不需要被认证
.antMatchers("/error.html").permitAll()
//login.html不需要被认证
.antMatchers("/login.html").permitAll()
//resources目录下的静态资源
.antMatchers("/static/js/**", "/static/css/**", "/static/images/**").permitAll()
//regexMatchers放行的使用
//.regexMatchers(".+[.]png").permitAll()
//.regexMatchers(HttpMethod.GET,"/demo").permitAll()
//.mvcMatchers("/demo").servletPath("/xxxx").permitAll()
//如果不习惯mvcMatchers还可以用antMatchers设置等效
.antMatchers("/xxxx/demo").permitAll()
//任何目录下的png图片
//.antMatchers("/**/*.png").permitAll()
//判断用户是否有admin权限
//.antMatchers("/main1.html").hasAuthority("admin")
//可以设置这个页面,多个权限去访问
//.antMatchers("/main1.html").hasAnyAuthority("admin","normall")
//角色判断
//.antMatchers("/main1.html").hasRole("abc")
//判断有多个角色权限
//.antMatchers("/main1.html").hasAnyRole("abc,abC")
//IP地址判断
.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
//所有请求都必须被认证,必须登录之后能被访问
//.anyRequest().authenticated();
//自定义访问权限
.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");
//关闭csrf防护
http.csrf().disable();
//异常处理
http.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler);
}
//创建PasswordEncoder实例
@Bean
public PasswordEncoder getPw(){
return new BCryptPasswordEncoder();
}
}
给登录的用户加一个main.html的权限:
package com.xxxx.springsecuritydemo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder pw;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1.查询数据库判断用户名是否存在,如果不存在就会抛出UsernameNotFoundException异常
//这里进行模拟,用户名为admin
if (!"admin".equals(username)) {
throw new UsernameNotFoundException("用户名不存在");
}
//2.//如果存在,把查询出来的密码(注册时已经经过加密) 进行解析,或者直接把密码放到构造方法
String password=pw.encode("123");
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normall,ROLE_abc,/main.html"));
}
}