导入依赖框架
web 框架(spring-boot-starter-web)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
springSecurity 框架(spring-boot-starter-security)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
导入框架之后、当前应用已经具备验证功能
-
用户名默认为user、密码为启动窗口打印信息
-
默认登陆页(存在问题、每次需要记录登录密码)
-
配置文件配置固定用户名、密码
自定义功能实现(用户信息从数据库获取)
方式一:
-
导入数据源依赖 mysql\mybatis,配置数据源信息
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
-
直接配置查询 sql (select username,password from s_usr where username = ?)
package com.bu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
/**
* @author haizhuangbu
* @date 2024/5/15 16:35
* @mark WebSecurityConfigImpl
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfigImpl extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
// 配置数据源
.dataSource(dataSource)
// 查询sql
.usersByUsernameQuery("select username,password,'Y' enabled from s_usr where username = ?")
.authoritiesByUsernameQuery("select username,authority\n" +
"from authorizes where username = ?");
}
// 不进行解密、直接对比
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
-
此时用户信息就是去数据库查询的 (用户信息表 创建包含用户密码关键字段即可)
create table s_usr
(
user_id varchar(36) not null
primary key,
username varchar(36) null,
password varchar(36) null,
create_time datetime null,
modify_time datetime null,
enable char(2) null comment 'Y 生效 N 失效'
);
create table authorizes
(
username varchar(36),
authority varchar(36)
);
insert into s_usr
values ('1', 'admin', 'admin', now(), now(), 'Y');
方式二:
-
导入数据源配置信息同方式一相同
-
重写springSecurity 的几个组件
- UserDetailsService 用户信息查询接口(内部具体编写查询逻辑 )
package com.bu.config;
import com.bu.sys.user.dao.UserDetailsDao;
import com.bu.sys.user.dto.SUsrDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
* @author haizhuangbu
* @date 2024/5/15 16:22
* @mark AuthUserServiceImpl
*/
@Component
public class AuthUserServiceImpl implements UserDetailsService {
@Autowired
private UserDetailsDao userDetailsDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SUsrDto sUsrDto = userDetailsDao.findSUsrByUsername(username);
return sUsrDto;
}
}
package com.bu.sys.user.dao;
import com.bu.sys.user.dto.SUsrDto;
import org.apache.ibatis.annotations.Select;
/**
* @author haizhuangbu
* @date 2024/5/15 17:15
* @mark UserDetailsDao
*/
public interface UserDetailsDao {
@Select("select * from s_usr where username = #{username}")
SUsrDto findSUsrByUsername(String username);
}
- PasswordEncoder 加解密工具
// 不进行解密、直接对比
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
- UserDetail 用户信息
package com.bu.sys.user.dto;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* @author haizhuangbu
* @date 2024/5/15 17:16
* @mark SUsrDto
*/
public class SUsrDto implements UserDetails {
private String username;
private String password;
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
- AuthenticationProvider 验证流程
package com.bu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.Component;
/**
* @author haizhuangbu
* @date 2024/5/15 17:20
* @mark UserAutorizedServiceImpl
*/
@Component
public class UserAuthorizedServiceImpl implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 查询用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName());
if (userDetails == null) {
throw new UsernameNotFoundException("用户信息不存在");
}
if (!passwordEncoder.matches(userDetails.getPassword(), (String) authentication.getCredentials())) {
throw new UsernameNotFoundException("密码不正确");
}
return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword());
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
-
验证组件交给springSecurity (同数据源方式类似)
package com.bu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
/**
* @author haizhuangbu
* @date 2024/5/15 16:35
* @mark WebSecurityConfigImpl
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfigImpl extends WebSecurityConfigurerAdapter {
@Autowired
private UserAuthorizedServiceImpl userAuthorizedService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(userAuthorizedService);
}
}
配置其他信息(成功跳转、失败跳转....)
// 非页面列表页可以无权限访问外、其他都需要验证
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// permitAll 放行路径
.antMatchers("/login", "/blogs/listAllBlogs", "/blogs/listBloggerInfo", "/theme/listAll")
.permitAll()
.anyRequest().authenticated()
.and().formLogin() // 默认 login 登陆路径
.failureHandler(failLoginHandler)// 成功处理逻辑
// .defaultSuccessUrl("/success")
// .failureUrl("/error")
.and().addFilterAfter(tokenFilter, BasicAuthenticationFilter.class)
;
http.csrf().disable();
http.cors().disable();
}
自定义登陆页面(成功失败页面)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/loginSuccess").permitAll()
.anyRequest().authenticated().and()
.formLogin()// 采用默认登陆页方式
// 默认登陆页面
.loginPage("/loginPage")
// 对应 form 表单提交路径
.loginProcessingUrl("/doLogin")
// 成功
.successHandler(authSuccessHandler)
// 失败
.failureHandler(authFailHandler)
.permitAll()
.and().csrf().disable().httpBasic()
;
}
package com.bu.config;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
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;
/**
* @author haizhuangbu
* @date 2024/5/16 13:36
* @mark AuthSuccessHandler
*/
@Component
public class AuthSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.printf("{message:登陆成功}");
writer.flush();
}
}
package com.bu.config;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
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;
/**
* @author haizhuangbu
* @date 2024/5/16 14:50
* @mark AuthFailHandler
*/
@Component
public class AuthFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.printf("{message:登陆失败}");
writer.flush();
}
}
详细配置思维导图