1.引入依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
mysql
mysql-connector-java
8.0.33
runtime
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.3.1
2.配置 application.properties
server.port=8086
#
spring.application.name=security-sample
spring.main.allow-bean-definition-overriding=true
spring.mvc.static-path-pattern=/static/**
# thymeleaf 配置
spring.thymeleaf.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# 数据库配置
spring.datasource.name=defaultDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/db_plain?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root123
# 连接池配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=8
spring.datasource.hikari.minimum-idle=4
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=50000
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.pool-name=HikariCP
# mybatis 配置
mybatis.mapper-locations=classpath:mappers/*xml
mybatis.type-aliases-package=com.sky.biz.entity
mybatis.configuration.map-underscore-to-camel-case=true
3.定制开发 - 认证流程的 UserDetailsService
说明:UserDetailsService 负责在 Security 框架中从数据源中查询出2大主要信息,
分别为:认证信息(账号、密码)、授权信息(角色列表、权限列表)。随后将这些信息封装为 UserDetails 对象返回
留作后续进行登录认证以及权限判断。
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermsService permsService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
BizUser bizUser = userService.queryByUserName(username);
// 用户存在,查询封装用户的"角色与权限"信息到 UserDetails中,通常自定义封装对象,继承 UserDetails的子类 User
if(bizUser != null) {
// 使用 authorityList 封装角色权限信息
List authorityList = new ArrayList<>();
// 查询当前用户 - 角色信息
List roleList = roleService.getRoleListByUserId(bizUser.getId());
for (Role role : roleList) {
authorityList.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode()));
}
// 查询当前用户 - 权限信息
List permsList = permsService.getPermsListByUserId(bizUser.getId());
for (Perms perm : permsList) {
authorityList.add(new SimpleGrantedAuthority(perm.getPermCode()));
}
return new SecurityUser(bizUser, authorityList);
}
return null;
}
}
4.定义封装安全信息的实体类:SecurityUser
public class SecurityUser extends User {
private BizUser bizUser;
public SecurityUser(BizUser user, Collection extends GrantedAuthority> authorities) {
super(user.getUserName(), user.getPassword(), true,true, true, true, authorities);
this.bizUser = user;
}
public SecurityUser(String username, String password, Collection extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
// get && set
public BizUser getBizUser() {
return bizUser;
}
public void setBizUser(BizUser bizUser) {
this.bizUser = bizUser;
}
}
5.自定义密码校验的类:PasswordEncoder,这里自由发挥,根据各自公司安全需要自定义
主要就是重写 encode(CharSequence rawPassword) 和 matches(CharSequence rawPassword, String encodedPassword) 方法
如果不想自定义就配置成spring-security提供的 BCryptPasswordEncoder 来处理密码加密与校验
6.配置Security
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级别的细粒度权限控制
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
// 配置对 HTTP 请求的安全拦截处理
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/static/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.and().csrf().disable()
.formLogin().loginPage("/login").loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/main")
.failureUrl("/fail")
.permitAll();
// "/login","/main"与"/fail",都是对应 html页面访问controller跳转路径
// 用户权限不够,处理并返回响应
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
String header = request.getHeader("X-Requested-With");
if("XMLHttpRequest".equals(header)) {
response.getWriter().print("403"); // 返回ajax 请求对应的 json格式
} else {
request.getRequestDispatcher("/error403").forward(request, response);
}
}
});
}
@Bean
public MyPasswordEncoder passwordEncoder() {
return new MyPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new passwordEncoder());
// 或者:auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
7.使用加密时:
@Autowired
private MyPasswordEncoder passwordEncoder;
// 方法1:
String encodePwd = passwordEncoder.encode(user.getPassword());
// 方法2:
String encodePwd = new BCryptPasswordEncoder().encode(user.getPassword());
user.setPassword(encodePwd);
userMapper.save(user);
大体整合完成!