一、引入相关依赖
<!--springSceurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
二、基于默认用户名、密码以及登录测试
1.编写controller
package com.ljy.myspringbootlogin.controller;
import com.ljy.myspringbootlogin.commont.Reuslt;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/springSecurity01")
public class TestSpringSecurity01Controller {
@RequestMapping("/test")
private Reuslt<String> test(){
String a="这是一个使用springSecurity默认的账号密码的案例!!!";
return Reuslt.ok(a);
}
}
默认的用户名是user,默认的密码在控制台打印。
3.浏览器访问
当我们使用springsecurity作为安全框架的时候,访问自己编写的接口的时候,会默认跳转到springsecurity自带的登录页进行认证,认证完成之后才可以访问自己编写的接口。
三、基于配置文件配置账号密码测试
1.在配置文件中增加配置
spring.security.user.name=admin
spring.security.user.password=123
2.重新启动访问
四、 基于数据库是登录(mybatis-plus)
数据库表的创建根据RBAC模型设计
1.创建用户表(user)
CREATE TABLE `user` (
`id` int NOT NULL COMMENT 'id',
`username` varchar(255) DEFAULT NULL COMMENT '账号',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`is_deleted` int DEFAULT NULL COMMENT '是否删除',
PRIMARY KEY (`id`)
)
2.创建角色表(role)
CREATE TABLE `role` (
`id` int NOT NULL COMMENT 'id',
`role_name` varchar(255) DEFAULT NULL COMMENT '角色名称',
`role_tag` varchar(255) DEFAULT NULL COMMENT '角色标签',
`is_deleted` int DEFAULT NULL COMMENT '是否删除',
PRIMARY KEY (`id`)
)
3.创建权限表(menu)
CREATE TABLE `menu` (
`id` int NOT NULL COMMENT 'id',
`menu_name` varchar(255) DEFAULT NULL COMMENT '权限名称',
`menu_tag` varchar(255) DEFAULT NULL COMMENT '权限标签',
`parent_id` int DEFAULT NULL COMMENT '父id',
`menu_type` int DEFAULT NULL COMMENT '权限类型(1:目录,2:菜单,3:按钮)',
`is_deleted` int DEFAULT NULL COMMENT '是否删除',
PRIMARY KEY (`id`)
)
4.创建用户角色表(user_role)
CREATE TABLE `user_role` (
`id` int NOT NULL COMMENT 'id',
`user_id` int DEFAULT NULL COMMENT '用户id',
`role_id` int DEFAULT NULL COMMENT '角色id',
PRIMARY KEY (`id`)
)
5.创建角色权限表(role_menu)
CREATE TABLE `role_menu` (
`id` int NOT NULL COMMENT 'id',
`role_id` int DEFAULT NULL COMMENT '角色id',
`menu_id` int DEFAULT NULL COMMENT '权限id',
PRIMARY KEY (`id`)
)
6.pom.xml文件引入相关依赖
<!--springSceurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 引入mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>
7.配置文件配置数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/my_springboot?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
8.创建对应实体类
8.1 UserModel:必须实现UserDetails 接口,用来进行安全认证
package com.ljy.myspringbootlogin.model;
import com.baomidou.mybatisplus.annotation.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
/**
* 当使用springsecurity做为安全框架的时候,用户model必须实现UserDetails,因为springsecuritty会使用UserDetails里面的权限信息和账号密码等信息
*/
@TableName("user")
public class UserModel implements Serializable,UserDetails {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 账号
* @return
*/
@TableField("username")
private String username;
/**
* 密码
* @return
*/
@TableField("password")
private String password;
/**
* 是否删除
* @TableLogic 是mybatis-plus提供的一个逻辑删除,默认0是未删除,1是删除
* @return
*/
@TableLogic
@TableField("is_deleted")
private Long isDeleted;
/**
* 权限
* @return
*/
@TableField(exist = false)
private List<GrantedAuthority> authorities;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public Long getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Long isDeleted) {
this.isDeleted = isDeleted;
}
public String getUsername() {
return username;
}
/**
* 判断账号是否未过期
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
*判断账号是否未被锁定
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 判断密码是否未过期
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 判断账号是否启用
* @return
*/
@Override
public boolean isEnabled() {
return true;
}
/**
* 权限
* @return
*/
@Override
public List<GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<GrantedAuthority> authorities) {
this.authorities = authorities;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "UserModel{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", isDeleted=" + isDeleted +
'}';
}
public UserModel(Long id, String username, String password, Long isDeleted) {
this.id = id;
this.username = username;
this.password = password;
this.isDeleted = isDeleted;
}
public UserModel() {
}
}
9.编写controller
package com.ljy.myspringbootlogin.controller;
import com.ljy.myspringbootlogin.commont.Reuslt;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.service.IUserService;
import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @RestController: Spring Framework 中的一个注解,用于简化创建 RESTful web services 的过程。
* 这个注解是 Spring 4.0 之后引入的,它结合了 @Controller 和 @ResponseBody 两个注解的功能
* 代表着 LoginController 被当作控制器,直接返回一个响应体(Response Body),而不是返回一个视图(View)名称
* @RequestMapping("/user"): Spring Framework 中的一个注解,用于将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上
* 如果作用于类上面,那么代表着这个类的所有方法都需要使用 '/user' 来作为前缀进行请求,其中可以指定请求方式"
* @RequestMapping(value = "/user",method = RequestMethod.POST)
*/
@RestController
@RequestMapping(value = "/user")
public class LoginController {
@Autowired
IUserService iUserService;
/**
* 登录接口
* @param username
* @param password
* @return UserModel
*/
@RequestMapping("/login")
public Reuslt<UserModel> login(@Param("username") String username,@Param("password") String password){
Reuslt<UserModel> login = iUserService.login(username, password);
return login;
}
}
10.编写service
package com.ljy.myspringbootlogin.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ljy.myspringbootlogin.commont.Reuslt;
import com.ljy.myspringbootlogin.model.UserModel;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface IUserService extends IService<UserModel> {
Reuslt<UserModel> login(String username, String password);
}
11.编写impl
package com.ljy.myspringbootlogin.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljy.myspringbootlogin.commont.Reuslt;
import com.ljy.myspringbootlogin.mapper.UserMapper;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserModel> implements IUserService {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Reuslt<UserModel> login(String username, String password) {
System.out.println("进入serviceimpl");
System.out.println(username);
System.out.println(password);
//传入用户名和密码
UsernamePasswordAuthenticationToken tocken = new UsernamePasswordAuthenticationToken(username, password);
System.out.println("tocken"+tocken);
//实现登录,此时就会调用loadUserByName
//authenticate其实就是userdetails
Authentication authenticate = null;
try{
authenticate= authenticationManager.authenticate(tocken);
}catch (BadCredentialsException e){
return Reuslt.error(500,"用户名或者密码错误");
}
UserModel principal = (UserModel)authenticate.getPrincipal();
System.out.println("principal:"+principal);
return Reuslt.ok(principal);
}
}
12.编写mapper
package com.ljy.myspringbootlogin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ljy.myspringbootlogin.model.UserModel;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<UserModel> {
UserModel getList(String username);
}
13.编写mapper.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ljy.myspringbootlogin.mapper.UserMapper">
<select id="getList" resultType="com.ljy.myspringbootlogin.model.UserModel">
select a.*
from user a
where a.username=#{username}
</select>
</mapper>
14.自定义一个认证逻辑(springSecuriyService),必须集成UserDetailsService,因为认证逻辑在UserDetailsService
package com.ljy.myspringbootlogin;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ljy.myspringbootlogin.mapper.UserMapper;
import com.ljy.myspringbootlogin.model.UserModel;
import lombok.extern.slf4j.Slf4j;
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.Service;
@Service
@Slf4j
public class springSecurityService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* 根据用户名查询用户
*/
//通过账号查询用户
System.out.println("进入!!!!");
//
UserModel user = userMapper.getList(username);
System.out.println("user:"+user.toString());
//如果没有查询到用户,则抛出异常
if(user == null){
throw new UsernameNotFoundException("账号或密码错误!");
}
//TODO 后续可以查角色和权限
return user;
}
}
15.将自定义的逻辑通过配置传递到springsecurity中,替换原有的认证逻辑
package com.ljy.myspringbootlogin.config;
import com.ljy.myspringbootlogin.springSecurityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@EnableWebSecurity //开启springSecurity,会注册大量的过滤器链
@Configuration
public class springSecurityConfig {
@Autowired
private springSecurityService springSecurityService;
/**
* 配置过滤器链
* @param http
* @return
* @throws Exception
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http.csrf().disable();//跨域漏洞防御:关闭
http.cors().disable();//跨域拦截关闭
http.authorizeHttpRequests()
.antMatchers("/user/**").permitAll()
.anyRequest().authenticated();
return http.build();
}
/**
* AuthenticationManager:负责认证,也就是认证规则
* DaoAuthenticationProvider:负责将springSecurityService和passwordEncoder放进AuthenticationManager中
* @return
*/
@Bean
public AuthenticationManager authenticationManager(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(springSecurityService);
//关联使用的密码编码器
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
//将daoAuthenticationProvider放到ProviderManager中
ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
return providerManager;
}
/**
* 密码编码器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
五、总结
1.自定义登录认证流程
(1)正常编写登录的三层架构,唯一的区别是impl文件中不能进行用户查询功能,因为这个功能需要使用springSecurity去完成。
(2)需要创建一个service实现springSecurity中的UserDetailsService接口,在这里鞋查询用户的逻辑。
(3)创建一个springSecurity的配置类,将自定义的service传入到AuthenticationManager(springsecurity是通过AuthenticationManager来实现认证的)中,会进行判断。
(4)在(1)中的impl文件里引入(3)创建的配置类,调用authenticate方法进行认证和获取结果。