一、环境搭建
1、环境参数
spring-boot 2.2.7
lombok 1.18.10
mybatis-plus 3.3.1
2、maven依赖
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
3、创建springboot项目,并导入pom.xml依赖
项目结构
启动类
@SpringBootApplication
@MapperScan("com.springboot.security.mapper")
public class SpringbootSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSecurityApplication.class, args);
}
}
二、config配置
1、WebSecurityConfig配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailService customUserDetailService;
@Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
.successHandler(customAuthenticationSuccessHandler)
.failureHandler(customAuthenticationFailureHandler)
.and()
.logout()
.permitAll();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**","/js/**");
}
}
2、CustomUserDetailService
@Service("userDetailsService")
public class CustomUserDetailService implements UserDetailsService {
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysUserRoleService sysUserRoleService;
@Autowired
private ISysRoleService sysRoleService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities = new ArrayList<>();
QueryWrapper userWrapper = new QueryWrapper();
userWrapper.eq("name",username);
List<SysUser> users = sysUserService.list(userWrapper);
if(users == null || users.size() == 0){
throw new RuntimeException("该用户不存在");
}
SysUser sysUser = users.get(0);
QueryWrapper userRoleWrapper = new QueryWrapper();
userRoleWrapper.eq("user_id",sysUser.getId());
List<SysUserRole> list = sysUserRoleService.list(userRoleWrapper);
if(list == null && list.size() == 0){
return new User(username,sysUser.getPassword(),authorities);
}
List<Integer> roleIds = list.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
for(Integer roleId : roleIds){
SysRole sysRole = sysRoleService.getById(roleId);
authorities.add(new SimpleGrantedAuthority(sysRole.getName()));
}
return new User(username,sysUser.getPassword(),authorities);
}
}
3、CustomAuthenticationFailureHandler
登录认证失败
@Slf4j
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
log.info("用户登录失败");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
}
}
4、CustomAuthenticationSuccessHandler
登录认证成功
@Slf4j
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("用户{}登录成功",authentication.getName());
response.sendRedirect("/");
}
}
5、init
springboot启动时,初始化容器时会执行,有没有这个都行
@Slf4j
@Component
public class Init implements ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
log.info("init=========={}", LocalDateTime.now());
}
}
6、application.yml配置
server:
port: 9000
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot-test?serverTimeZone=GMT+8&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: xxx
三、业务逻辑代码
1、LoginController
@Slf4j
@Controller
public class LoginController {
@RequestMapping("/")
public String show(Model model){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String name = authentication.getName();
model.addAttribute("name",name);
return "show.html";
}
@RequestMapping("/login")
public String showLogin(){
return "login.html";
}
@RequestMapping("/admin")
@ResponseBody
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String checkAdmin(){
return "该用户具有ROLE_ADMIN角色";
}
@RequestMapping("/user")
@ResponseBody
@PreAuthorize("hasRole('ROLE_USER')")
public String checkUser(){
return "该用户具有ROLE_USER角色";
}
}
2、SysUser
pojo
@Data
public class SysUser {
private Integer id;
private String name;
private String password;
}
service
public interface ISysUserService extends IService<SysUser> {
}
@Service("sysuserservice")
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
}
mapper
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}
3、SysRole
pojo
@Data
public class SysRole {
private Integer id;
private String name;
}
service
public interface ISysRoleService extends IService<SysRole> {
}
@Service("sysroleservice")
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
}
mapper
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRole> {
}
4、SysUserRole
pojo
@Data
public class SysUserRole {
private Integer userId;
private Integer roleId;
}
service
public interface ISysUserRoleService extends IService<SysUserRole> {
}
@Service("sysuserroleservice")
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleMapper, SysUserRole> implements ISysUserRoleService {
}
mapper
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
}
5、login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post" action="/login">
<div>
用户名:<input type="text" name="username" />
</div>
<div>
密码:<input type="password" name="password" />
</div>
<div>
<button type="submit">登录</button>
</div>
</form>
</body>
</html>
6、show.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>角色验证</title>
</head>
<body>
<h1>登录成功</h1>
<div>
<a href="/admin">检测ROLE_ADMIN角色</a>
</div>
<div>
<a href="/user">检测ROLE_USER角色</a>
</div>
<div>
<button onclick="window.location.href='/login'">退出登录</button>
</div>
</body>
</html>
四、数据库
1、sys_user表
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
2、sys_role表
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、sys_user_role
CREATE TABLE `sys_user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`role_id`),
KEY `fk_role_id` (`role_id`),
CONSTRAINT `fk_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4、初始化SQL
INSERT INTO `sys_user`(`id`, `name`, `password`) VALUES (1, 'admin', '$2a$06$Vbtkylrumz6/ozMVE4x0Xuh7yCycKrhZGYrYjY2ejF3CudNhZImjO');
INSERT INTO `sys_user`(`id`, `name`, `password`) VALUES (2, 'zhangsan', '$2a$06$Vbtkylrumz6/ozMVE4x0Xuh7yCycKrhZGYrYjY2ejF3CudNhZImjO');
INSERT INTO `sys_role`(`id`, `name`) VALUES (1, 'ROLE_ADMIN');
INSERT INTO `sys_role`(`id`, `name`) VALUES (2, 'ROLE_USER');
INSERT INTO `sys_user_role`(`user_id`, `role_id`) VALUES (1, 1);
INSERT INTO `sys_user_role`(`user_id`, `role_id`) VALUES (2, 2);
五、启动测试
登录页面
用户 admin 密码 123456
用户 zhangsan 密码 123456
登录成功以后
admin具有 admin角色
zhangsan具有user角色