springboot整合springsecurity最完整,只看这⼀篇就够了
本⼈结合其他博客和⾃⼰查询的资料,⼀步⼀步实现整合了security安全框架,其中踩过不少的坑,也有遇到许多不懂的地⽅,为此做个记录。
开发⼯具:ide、数据库:mysql5.7、springboot版本:2.3.7
个⼈对Spring Security的执⾏过程⼤致理解(仅供参考)
使⽤Spring Security很简单,只要在l⽂件中,引⼊spring security的依赖就可以了
pom配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
这个时候我们不在配置⽂件中做任何配置,随便写⼀个Controller
@RestController
public class TestController {
@GetMapping("/hello")
public String request() {
return "hello";
}
}
启动项⽬,我们会发现有这么⼀段⽇志
这⾥⾯的⽤户名和密码是什么呢?此时我们需要输⼊⽤户名:user,密码则为之前⽇志中的"19262f35-9ded-49c2-a8f6-5431536cc50c",输⼊之后,我们可以看到此时可以正常访问该接⼝
在⽼版本的Springboot中(⽐如说Springboot 1.x版本中),可以通过如下⽅式来关闭Spring Security的⽣效,但是现在Springboot 2中已经不再⽀持
security:
basic:
enabled: false
springboot2.x后可以在启动类中设置
1、配置基于内存的⾓⾊授权和认证信息
1.1⽬录
1.2 WebSecurityConfg配置类
Spring Security的核⼼配置类是 WebSecurityConfigurerAdapter抽象类,这是权限管理启动的⼊⼝,这⾥我们⾃定义⼀个实现类去实现它。
/**
* @Author qt
* @Date 2021/3/25
* @Description SpringSecurity安全框架配置
*/
@Configuration
@EnableWebSecurity//开启Spring Security的功能
//prePostEnabled属性决定Spring Security在接⼝前注解是否可⽤@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接⼝
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 基于内存的⽅式,创建两个⽤户admin/123456,user/123456
* */
auth.inMemoryAuthentication()
.withUser("admin")//⽤户名
.password(passwordEncoder().encode("123456"))//密码
.roles("ADMIN");//⾓⾊
auth.inMemoryAuthentication()
.withUser("user")//⽤户名
.password(passwordEncoder().encode("123456"))//密码
.roles("USER");//⾓⾊
}
/**
* 指定加密⽅式
*/
@Bean
public PasswordEncoder passwordEncoder(){
// 使⽤BCrypt加密密码
return new BCryptPasswordEncoder();
}
}
1.3 MainController控制器接⼝
/**
* @Author qt
* @Date 2021/3/25
* @Description 主控制器
public class MainController {
@GetMapping("/hello")
public String printStr(){
System.out.println("hello success");
return "Hello success!";
}
}
这样重新运⾏后我们就可以通过admin/123456、user/123456两个⽤户登录了。
当然,你也可以基于配置⽂件创建⽤户账号,在l中添加:
2、配置基于数据库的认证信息和⾓⾊授权
2.1 ⽬录
2.2 CustomUserDetailsService实现类
UserDetailsService是需要实现的登录⽤户查询的service接⼝,实现loadUserByUsername()⽅法,这⾥我们⾃定义CustomUserDetailsService实现类去实现UserDetailsService接⼝
/**
* @Author qt
* @Date 2021/3/25
* @Description
*/
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Resource
private UserInfoService userInfoService;
@Resource
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
/**
* 1/通过userName 获取到userInfo信息
* 2/通过User(UserDetails)返回details。
*/
//通过userName获取⽤户信息
UserInfo userInfo = UserInfoByUsername(userName);
if(userInfo == null) {
throw new UsernameNotFoundException("not found");
}
//定义权限列表.
List<GrantedAuthority> authorities = new ArrayList<>();
// ⽤户可以访问的资源名称(或者说⽤户所拥有的权限)注意:必须"ROLE_"开头
authorities.add(new SimpleGrantedAuthority("ROLE_"+ Role()));
User userDetails = new UserName(),Password()),authorities);
return userDetails;
}
}
WebSecurityConfg配置类:
/**
* @Author qt
* @Date 2021/3/25
* @Description SpringSecurity安全框架配置
*/
@Configuration
@EnableWebSecurity//开启Spring Security的功能
//prePostEnabled属性决定Spring Security在接⼝前注解是否可⽤@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接⼝
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
/**
* 指定加密⽅式
*/
@Bean
public PasswordEncoder passwordEncoder(){
// 使⽤BCrypt加密密码
return new BCryptPasswordEncoder();
}
}
对于通过userName获取⽤户信息的服务层,持久层和数据库语句就不介绍了,这⾥使⽤的是SSM框架,使⽤mybaits。
2.3 数据库设计
⾓⾊表 roles
⽤户表 user
⽤户⾓⾊关系表 roles_user
3、⾃定义表单认证登录
3.1 ⽬录
3.2 WebSecurityConfg核⼼配置类
* @Description spring-security权限管理的核⼼配置
*/
@Configuration
@EnableWebSecurity//开启Spring Security的功能
/
/prePostEnabled属性决定Spring Security在接⼝前注解是否可⽤@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接⼝
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
@Resource
private AuthenticationSuccessHandler loginSuccessHandler; //认证成功结果处理器
@Resource
private AuthenticationFailureHandler loginFailureHandler; //认证失败结果处理器
//http请求拦截配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();//开启运⾏iframe嵌套页⾯
http//1、配置权限认证
.authorizeRequests()
//配置不拦截路由
.antMatchers("/500").permitAll()
.antMatchers("/403").permitAll()
.antMatchers("/404").permitAll()
.antMatchers("/login").permitAll()
.anyRequest() //任何其它请求
.authenticated() //都需要⾝份认证
.and()
//2、登录配置表单认证⽅式
.
formLogin()
.loginPage("/login")//⾃定义登录页⾯的url
.usernameParameter("username")//设置登录账号参数,与表单参数⼀致
.passwordParameter("password")//设置登录密码参数,与表单参数⼀致
// 告诉Spring Security在发送指定路径时处理提交的凭证,默认情况下,将⽤户重定向回⽤户来⾃的页⾯。登录表单form中action的地址,也就是处理认证请求的路径,
// 只要保持表单中action和HttpSecurity⾥配置的loginProcessingUrl⼀致就可以了,也不⽤⾃⼰去处理,它不会将请求传递给Spring MVC和您的控制器,所以我们就不需要⾃⼰再去写⼀个/user/login的控制器接⼝了 .loginProcessingUrl("/user/login")//配置默认登录⼊⼝
.defaultSuccessUrl("/index")//登录成功后默认的跳转页⾯路径
.failureUrl("/login?error=true")
.successHandler(loginSuccessHandler)//使⽤⾃定义的成功结果处理器
.
failureHandler(loginFailureHandler)//使⽤⾃定义失败的结果处理器
.and()
//3、注销
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.permitAll()
.and()
//4、session管理
.sessionManagement()
.invalidSessionUrl("/login") //失效后跳转到登陆页⾯
/
/单⽤户登录,如果有⼀个登录了,同⼀个⽤户在其他地⽅登录将前⼀个剔除下线
//.maximumSessions(1).expiredSessionStrategy(expiredSessionStrategy())
//单⽤户登录,如果有⼀个登录了,同⼀个⽤户在其他地⽅不能登录
//.maximumSessions(1).maxSessionsPreventsLogin(true) ;
.and()
//5、禁⽤跨站csrf攻击防御
.csrf()
.disable();
}
@Override
public void configure(WebSecurity web) throws Exception {
/
/配置静态⽂件不需要认证
web.ignoring().antMatchers("/static/**");
}
/**
* 指定加密⽅式
*/
@Bean
public PasswordEncoder passwordEncoder(){
// 使⽤BCrypt加密密码
return new BCryptPasswordEncoder();
}
}
踩坑点1:登录页⾯接⼝/login和登录验证接⼝/user/login,这⾥是⾃⼰之前⼀直搞错的重点,这⾥就⽤⽹上的图⽚展⽰了
踩坑点2:springboot配置spring security 静态资源不能访问
security的配置:在类WebSecurityConfig继承WebSecurityConfigurerAdapter,这个类是我们在配置security的时候,对我们请求的url及权限规则的⼀些认证
配置。具体的不说了,这⾥主要是静态资源的问题。
在这个类中我们会重写⼀些⽅法,其中就有⼀个⽅法,可以为我们配置⼀下静态资源不需要认证。
@Override
public void configure(WebSecurity web) throws Exception {
//配置静态⽂件不需要认证
web.ignoring().antMatchers("/static/**");
}
页⾯的引⽤如下:
<link rel="stylesheet" th:href="@{static/layui/css/layui.css}">
之后我们启动项⽬:看到css并没有⽣效
这时候仅仅通过spring security配置是不够的,我们还需要去重写addResourceHandlers⽅法去映射下静态资源,这个⽅法应该很熟悉了,我们通过springboot添加的时候就会
⽤到这个。
写⼀个类WebMvcConfig继承WebMvcConfigurationSupport,注意spring boot2版本和1版本是不⼀样的,spring boot1版本继承的WebMvcConfigurerAdapter在spring boot2版本中已经提⽰过
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 配置静态资源
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}
}
现在重新启动项⽬:css⽂件已经引⽤成功。
3.3 ErrorPageConfig 配置错误页⾯
/**
* @Author qt
* @Date 2021/3/25
* @Description 配置错误页⾯ 403 404 500 适⽤于 SpringBoot 2.x
*/
@Configuration
public class ErrorPageConfig {
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
WebServerFactoryCustomizer<ConfigurableWebServerFactory> webCustomizer = new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() { @Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage[] errorPages = new ErrorPage[] {
new ErrorPage(HttpStatus.FORBIDDEN, "/403"),
new ErrorPage(HttpStatus.NOT_FOUND, "/404"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500"),
};
factory.addErrorPages(errorPages);
}
};
return webCustomizer;
}
}
3.4 MainController 控制器
/**
* @Author qt
* @Date 2021/3/25
* @Description 主控制器
*/
@Controller
public class MainController {
private Logger logger = Logger(getClass());
@GetMapping("/login")
public String loginPage(){
System.out.println("login page");
return "login";
}
@GetMapping("/index")
@PreAuthorize("hasAnyRole('USER','ADMIN')")
public String index(){
System.out.println("index page");
return "index";
}
@GetMapping("/admin")
@PreAuthorize("hasAnyRole('ADMIN')")
public String printAdmin(){
System.out.println("hello admin");
return "admin";
}
@GetMapping("/user")
@PreAuthorize("hasAnyRole('USER','ADMIN')")
public String printUser(){
System.out.println("hello user");
return "user";
}
/**
* 不到页⾯
*/
@GetMapping("/404")
public String notFoundPage() {
return "/error/404";
}
/**
* 未授权
*/
@GetMapping("/403")
public String accessError() {
return "/error/403";
}
/**
* 服务器错误
*/
@GetMapping("/500")
public String internalError() {
return "/error/500";
}
}
3.5 UserInfoController ⽤户控制器
/
**
* @Author qt
* @Date 2021/3/25
* @Description
*/
@Controller
@RequestMapping("/user")
public class UserInfoController {
private Logger logger = Logger(getClass());
@Resource
private UserInfoService userInfoService;
@GetMapping("/getUserInfo")
@ResponseBody
public User getUserInfo(@RequestParam String username){
UserInfoByUsername(username);
}
}
SMM框架的其他部分就省略了,⾮这⾥重点。
3.6 CustomAccessDecisionManager ⾃定义权限决策管理器
/**
* @Author qt
* @Date 2021/3/31
* @Description ⾃定义权限决策管理器
*/
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {
/**
* @Author: qt
* @Description: 取当前⽤户的权限与这次请求的这个url需要的权限作对⽐,决定是否放⾏
* auth 包含了当前的⽤户信息,包括拥有的权限,即之前UserDetailsService登录时候存储的⽤户对象
* object 就是FilterInvocation对象,可以得到request等web资源。
* configAttributes 是本次访问需要的权限。即上⼀步的 MyFilterInvocationSecurityMetadataSource 中查询核对得到的权限列表
**/
@Override
public void decide(Authentication auth, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
Iterator<ConfigAttribute> iterator = collection.iterator();
while (iterator.hasNext()) {
if (auth == null) {
throw new AccessDeniedException("当前访问没有权限");
}
ConfigAttribute ca = ();
//当前请求需要的权限
String needRole = ca.getAttribute();
if ("ROLE_LOGIN".equals(needRole)) {
if (auth instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("未登录");
} else
return;
}
//当前⽤户所具有的权限
Collection<? extends GrantedAuthority> authorities = Authorities();
ssm框架实现登录功能for (GrantedAuthority authority : authorities) {
if (Authority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不⾜!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
3.7 CustomLogoutSuccessHandler 注销登录处理
/**
* @Author qt
* @Date 2021/3/31
* @Description 注销登录处理
*/
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
private Logger logger = Logger(getClass());
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("注销成功!");
//这⾥写你登录成功后的逻辑
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
}
}
3.8 LoginFailureHandler 登录失败处理
/**
* @Author qt
* @Date 2021/3/24
* @Description 登录失败处理
*/
@Component("loginFailureHandler")
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private Logger logger = Logger(getClass());
@Resource
private ObjectMapper objectMapper;
@Override
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论