SpringSecurity学习笔记(一)- 基于SpringBoot+SpringSecurity+Mybatis+MySQL+VUE的Demo(带源码下载)

本文适合有一定springboot、mybatis、mysql、vue经验的同学,一步一步教大家实现基于SpringSecurity+Mybatis+Mysql+vue的demo。内容包括后端开发,如新建Springboot应用、配置数据库等;前端配置,修改后端base地址;以及Nginx配置代理解决跨域问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面:本文是一步一步教大家实现一个基于SpringSecurity+Mybatis+Mysql+vue的demo,基于此可以快速开发自己的应用。

本文的目标读者是有一定springboot、mybatis、mysql、vue经验的同学看的,并不适合完全的小白,也不适合高手!

内容比较长,如果不想看,也可以直接去下载源码:(SpringSecurityDemo1)

如果想看SpringSecurity其他笔记,请见:SpringSecurity学习笔记

一、后端开发

1.新建Springboot应用

在本地新建springboot应用,如果有需要,可参看我之前的博文:使用IDEA新建springboot项目

2.添加依赖

在项目的pom文件中添加如下依赖

<!-- 引用spring security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- 引入thymeleaf  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- 引入mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

<!-- 引入mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

3.配置application.properties

在application.properties中配置如下:

#后端端口
server.port=8088

#配置数据库url,其中  security01 是数据库名,需要改成自己的
spring.datasource.url=jdbc:mysql://localhost:3306/security01?characterEncoding=UTF-8&useUnicode=true&useSSL=true&serverTimezone=UTC
#配置数据库用户
spring.datasource.username= 改成你的数据库用户名
#数据库密码
spring.datasource.password= 改成你的数据库密码
#mysql驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.mapper-locations:classpath:mapping/*.xml

4.配置mapper

(1)在org.security.mapper新建接口:UserMapper

@Mapper
public interface UserMapper {
    User loadUserByUsername(String username);

    List<Role> getRoleByUid(Integer id);
}

 (2)在ersources/mapping中新建xml:UserMapping.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="org.security.mapper.UserMapper">

    <select id="loadUserByUsername" resultType="org.security.user.User">
        SELECT
            *
        FROM `user`
        WHERE
        username=#{username}
    </select>

    <select id="getRoleByUid" resultType="org.security.user.Role">
        select *
        from role r,user_role ur
        where r.id=ur.rid
    </select>

</mapper>

5.配置数据库

(1)安装和配置mysql数据库,如有需要,请看:Mysql数据库安装和配置
(2)新建数据库表

DROP TABLE IF EXISTS security01.role;

CREATE TABLE security01.role
(
id  INT NOT NULL auto_increment,
name   VARCHAR (32),
nameZh VARCHAR (32),
PRIMARY KEY (id)
);

DROP TABLE IF EXISTS security01.`user`;

CREATE TABLE security01.`user`
(
id  INT NOT NULL auto_increment,
username  VARCHAR (32),
password  VARCHAR (255),
enabled  TINYINT,
accountNonExpired   TINYINT,
accountNonLocked    TINYINT,
credentialsNonExpired  TINYINT,
PRIMARY KEY (id)
);

DROP TABLE IF EXISTS security01.user_role;

CREATE TABLE security01.user_role
(
id  INT NOT NULL auto_increment,
uid INT,
rid INT,
PRIMARY KEY (id),
KEY uid (uid),
KEY rid (rid)
);

 (3)插入测试数据

因为insert的SQL比较简单,我这里就不写SQL了,各位可以自行写

1f90f49123b54ad2a69c70f75063c94f.png

6.写Entity

(1)写User类,实现UserDetails接口 

public class User implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean accountNonExpired;
    private Boolean accountNonLocked;
    private Boolean credentialsNonExpired;
    private List<Role> roles=new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities=new ArrayList<>();
        for (Role role:roles){
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

//    public Boolean getEnabled() {
//        return enabled;
//    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

//    public Boolean getAccountNonExpired() {
//        return accountNonExpired;
//    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setAccountNonExpired(Boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

//    public Boolean getAccountNonLocked() {
//        return accountNonLocked;
//    }

    public void setAccountNonLocked(Boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

//    public Boolean getCredentialsNonExpired() {
//        return credentialsNonExpired;
//    }

    public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

(2)写Role类

public class Role {

    private Integer id;

    private String name;

    private String nameZh;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}

7.重写MyUserDetailService
新建MyUserDetailsService,实现UserDetailsService接口: 

@Service
public class MyUserDetailsService  implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user=userMapper.loadUserByUsername(username);

        if(user==null){
            throw new UsernameNotFoundException("用户不存在");
        }

        user.setRoles(userMapper.getRoleByUid(user.getId()));
        System.out.println("##############password:"+user.getPassword());
        System.out.println("##############encode:"+passwordEncoder.encode(user.getPassword()));
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        return user;
    }
}

8.新建登录成功/失败的处理类

(1)新建成功后的处理MyAuthenticationSuccessHandler

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> resp=new HashMap<>();
        resp.put("status",200);
        resp.put("msg","登录成功");
        // 生成一个uuid返回给前端,用以作为token
        String token= UUID.randomUUID().toString().replace("-","");
        resp.put("token",token);
        ObjectMapper om=new ObjectMapper();
        String s=om.writeValueAsString(resp);
        response.getWriter().write(s);
    }
}

(2)新建失败后的处理MyAuthenticationFailureHandler

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> resp=new HashMap<>();

        resp.put("msg","登录失败!"+e.getMessage());
        System.out.println("登录失败:::"+e.toString());
        if(e instanceof BadCredentialsException){
            resp.put("status",401);
            resp.put("msg","用户名或密码不正确");
        }else{
            resp.put("status",500);
            resp.put("msg","系统内部错误");
        }
        ObjectMapper om=new ObjectMapper();
        String s=om.writeValueAsString(resp);
        response.getWriter().write(s);
    }
}

9.新建SecurityConfig

配置加密方式、登录方式,登录成功及失败后的处理 

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MyUserDetailsService myUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/mylogin.html")
                .loginProcessingUrl("/doLogin")
                // 登录成功后,返回json
                .successHandler(new MyAuthenticationSuccessHandler())
                //登录失败后,返回json
                .failureHandler(new MyAuthenticationFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and().cors()
                .and().logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessHandler((req,resp,auth)->{
                    resp.setContentType("application/json;charset=utf-8");
                    Map<String,Object> result=new HashMap<>();
                    result.put("status",200);
                    result.put("msg","注销成功");
                    ObjectMapper om=new ObjectMapper();
                    String s=om.writeValueAsString(result);
                    resp.getWriter().write(s);
                })
                .and()
                .csrf().disable();
    }
}

 二、前端配置

说明:我最近研究的是SpringSecurity,所以前端就拿之前自己的一个前端项目来改

1.修改后端base地址

修改main.js

Axios.defaults.baseURL = 'http://localhost:1025/'


说明:如果此时运行系统,会报跨域访问的错,其中一种方法是下面第三点,使用Nginx来配置代理。

三、Nginx配置代理

如果对Nginx不了解的同学,可参看我写的博文:Nginx实现跨域访问

1.修改nginx.conf配置

server{
		listen 1025;
		server_name localhost; 
		
		location / {
			add_header 'Access-Control-Allow-Origin' http://localhost:8080;
			add_header 'Access-Control-Allow-Credentials' true;
			add_header 'Access-Control-Allow-Headers' *;
			add_header 'Access-Control-Allow-Methods' *;
			add_header 'Access-Control-Expose-Headers' *; 
			
			proxy_pass http://localhost:8088;
		}
	}


2.说明

(1)http://localhost:8080 是前端的地址和端口,需要改成自己的
(2)http://localhost:8088 是后端的地址和商品,需要改成自己的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaokang2216

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值