SpringBoot第六篇-SpringBoot+Mybatis+SpringSecurity+JWT整合,开发工具IDea

一、新建SpringBoot项目

,选择Maven,插件选择SpringWeb

 

 二、引入springSecurity包

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

  

三、设置启动配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//AOP : 拦截器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //链式编程
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);
        //首页所有人可以访问,功能页只有对应权限的人才能访问
        //请求授权的规则
        http.authorizeRequests()
                .antMatchers("/logina/**").permitAll()
                .antMatchers("/hello/**").hasRole("vip1")   ;   //vip3用户只可以访问该路径下的页面
 
        //没有权限默认回到登录页面,需要开启登录的页面
        http.formLogin();
        //注销,开启了注销功能,跳到首页
        //http.logout().logoutSuccessUrl("/");
        //定制登录页
        http.formLogin();
        //开启记住我功能 cooke
        http.rememberMe();
    }
 
    //认证   springboot 2.1.X 可以直接使用
    //密码编码: passwordEncoder
    //在spring Security 5.0+ 新增了很多的加密方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这些数据正常应该从数据库中读取  现在测试的是从内存中读取的数据
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())   //加了一个密码的编码规则
                .withUser("LJ").password(new BCryptPasswordEncoder().encode("123")).roles("vip2", "vip3")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1", "vip2", "vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");
 
        //从数据库中获取数据
//        auth.jdbcAuthentication()
//                .dataSource(dataSource)
//                .withDefaultSchema()
//                .withUser(users.username("user").password("password").roles("USER"))
//                .withUser(users.username("admin").password("password").roles("USER","ADMIN"));
    }

  

 

四、创建控制器

@RestController,@RequestMapping,@GetMapping ,@Controller,@responbody的区别


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/logina")
public class LoginController {
 
    @GetMapping("/getlogin")
     public String getLogin(){
      return "登录";
     }
 
    @GetMapping("/getlogin2")
    public String getLogin2(){
        return "登录";
    }
}

  



 

  用户名:user,密码:

 

 输入以后登陆成功跳转到

 

 4、创建数据库表SysUser表,里面包含ID,username,password,rolename字段

 

 5、创建实体,放到entities包下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package cn.yinmingneng.yinmingneng.entities;
 
public class SysUser {
 
    private  Integer id;
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    public String getRolename() {
        return rolename;
    }
 
    public void setRolename(String rolename) {
        this.rolename = rolename;
    }
 
    private  String username;
    private  String password;
    private  String rolename;
}

 

6、创建usermapper对象,用于自动生成sysuser表的增删改查语句,此类是个接口,通过java动态代理生成,我使用配置文件生成增删改查语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Mapper
public interface SysUserMapper  {
 
    /**
     *  添加用户
     * @param sysUser
     * @return
     */
     int insertSysUser(SysUser sysUser);
 
    /**
     * 更新
     * @param sysUser
     * @return
     */
     int  updateSysUser(SysUser sysUser);
 
    /**
     * 删除用户
     * @return
     */
     int deleteSysUserById(Integer id);
 
     SysUser getAll(String userName);
 
}

7、编写映射的SysUserMapper.XML文件,id和接口的方法名一样,否则会报错,映射不到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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="cn.yinmingneng.yinmingneng.mapper.SysUserMapper"   >
 
 
 
    <sql id="selectSysUser"  >
        SELECT id ,username,password,rolename FROM sysuser
    </sql>
 
 
 
    <select id="getAll" parameterType="String" resultType="SysUser">
        SELECT id ,username,password,rolename FROM sysuser where username = #{username}
    </select>
 
    <insert id="insertSysUser" parameterType="SysUser"  >
        insert into sysuser (
        <if test="username != null and username != '' ">username,</if>
        <if test="password != null and password != '' ">password,</if>
        <if test="rolename != null and rolename != '' ">config_value,</if>
        )values(
        <if test="username != null and username != ''">#{username},</if>
        <if test="password != null and password != ''">#{password},</if>
        <if test="rolename != null and rolename != ''">#{rolename},</if>
        )
    </insert>
 
    <update id="updateSysUser" parameterType="SysUser">
        update sysuser
        <set>
            <if test="username != null and username != ''">username = #{username},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="rolename != null and rolename != ''">rolename = #{rolename}</if>
        </set>
        where id = #{id}
    </update>
 
    <delete id="deleteSysUserById"  parameterType="Integer">
        delete  from sysuser where id = #{id}
    </delete>
 
 
</mapper>

 8、服务层SysUserService代码,接口,方便接口化编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cn.yinmingneng.yinmingneng.Services;
 
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
 
 
public interface SysUserService {
 
    /**
     *  添加用户
     * @param sysUser
     * @return
     */
    public int insertSysUser(SysUser sysUser);
 
    /**
     * 更新
     * @param sysUser
     * @return
     */
    public int  updateSysUser(SysUser sysUser);
 
    /**
     * 删除用户
     * @return
     */
    public  int deleteSysUserById();
 
    public SysUser getAll(String userName);
}

 9、服务层实现类,SysUserServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package cn.yinmingneng.yinmingneng.Services.ServiceIml;
 
import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import cn.yinmingneng.yinmingneng.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class SysUserServiceImpl implements SysUserService {
 
    @Autowired
    SysUserMapper sysUserMapper;
 
    @Override
    public int insertSysUser(SysUser sysUser) {
        return   sysUserMapper.insertSysUser(sysUser);
    }
 
    @Override
    public int updateSysUser(SysUser sysUser) {
        return sysUserMapper.updateSysUser(sysUser);
    }
 
    @Override
    public int deleteSysUserById() {
        return 0;
    }
 
    @Override
    public SysUser getAll(String userName) {
 
        return sysUserMapper.getAll(userName);
    }
}

10、控制器测试层,永远测试带权限功能,不带权限功能,是否登录功能,匿名访问功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package cn.yinmingneng.yinmingneng.Controller;
 
import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class TestController {
 
    @Autowired
    SysUserService sysUserService;
 
    @RequestMapping("/gethello")
    public String getHello(){
        return "你好!";
 
    }
    /**
     *  添加用户
     * @param sysUser
     * @return
     */
    @RequestMapping("/insertsysuser")
     public int insertSysUser(SysUser sysUser)
    {
         return sysUserService.insertSysUser(sysUser);
     }
 
    /**
     * 更新
     * @param sysUser
     * @return
     */
    @RequestMapping("/a/updatesysuser")
    @PreAuthorize("hasRole('ROLE_USER')")
    public String  updateSysUser(SysUser sysUser){
        return "sucessUpdateSysUser";
    }
    @RequestMapping("/a/getall")
   public  SysUser getAll(String userName){
 
        return  sysUserService.getAll(userName);
   }
 
}

  

11、编写userdetailservie的实现类,该类用于实现userdetail对象,用于验证账号和密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package cn.yinmingneng.yinmingneng.config;
 
import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class UserDetailimpl implements UserDetailsService {
    @Autowired
    private SysUserService sysUserService;
    /**
     * 从数据库获取用户数据
     * @param s
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
 
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        SysUser user = sysUserService.getAll(s);
        // 判断用户是否存在
        if(user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        if(user.getUsername() != null && user.getUsername()!="")
        authorities.add(new SimpleGrantedAuthority(user.getRolename()));
        // 返回UserDetails实现类
        return new User(user.getUsername(), user.getPassword(), authorities);
    }
}

12、登录认证功能,spingSecutiry提供登录认证功能,请求/login会触发该方法,使用post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package cn.yinmingneng.yinmingneng.filters;
 
import cn.yinmingneng.yinmingneng.Services.TokenService;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 此类用于登录认证,有/login触发,需要post请求
 */
public class loginfilter   extends UsernamePasswordAuthenticationFilter {
    @Autowired
    private AuthenticationManager authenticationManager;
 
    public loginfilter(AuthenticationManager a){
        this.authenticationManager = a;
    }
    /**
     * 验证账号和密码
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
 
        String username= request.getParameter("username");
        String password = request.getParameter("password");
        //
       // return super.attemptAuthentication(request, response);
        //验证账号和密码
      return    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password,
              Collections.emptyList()));
    }
 
    /**
     * 认证成功,生成token,返回token
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
     //   super.successfulAuthentication(request, response, chain, authResult);
 
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
 
        String username= request.getParameter("username");
        Map<String,Object> map = new HashMap<>();
     //   map.put("id",sysUser.getId());
        map.put(TokenService.userKey,username);
       // map.put(TokenService.userKey,)
        String token =  TokenService.CreateToken(map);
        response.getWriter().write("认证成功,token:"+token);
    }
}

 

13、生成token,我使用了前后端分离功能,所以需要生成token,写了TokenService类,用于解析生成token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package cn.yinmingneng.yinmingneng.Services;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class TokenService {
    public static  String userKey="user_id";
    private  static  String signKey="rererrertretretrettretert";
 
    public static String CreateToken(Map<String, Object> map) {
        Date now = new Date(System.currentTimeMillis());
        String token = Jwts.builder()
                .setClaims(map) // 设置自定义数据
              //  .setIssuedAt(now) // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis()+ 1000*60*60*360)) // 设置过期时间
                //.setIssuer(issuer) // 设置签发者
               // .setSubject(subject) // 设置面向用户
                .signWith(SignatureAlgorithm.HS512, signKey)
                .compact();
        return  token;
    }
 
    /**
     * 解压token,获取里面的令牌,
     * 自动判断有效期
     */
    public static Map parseToken(String t) {
        try {
            return Jwts.parser()
                    .setSigningKey(signKey)
                    .parseClaimsJws(t.replace("Bearer ", ""))
                    .getBody();
        } catch (Exception e) {
            throw new IllegalStateException("Token验证失败:" + e.getMessage());
        }
    }
}

 13、携带token判断权限,token,用于判断token,判断成功就写入角色权限,该类无法自动注入SysUserService,需要手工注入,手工注入的代码在下面

BasicAuthenticationFilter 该类继承了oprefilter过滤器

 

package cn.yinmingneng.yinmingneng.filters;

import cn.yinmingneng.yinmingneng.Services.SysUserService;
import cn.yinmingneng.yinmingneng.Services.TokenService;
import cn.yinmingneng.yinmingneng.Utils.BeanFactoryUtil;
import cn.yinmingneng.yinmingneng.entities.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
private static final PathMatcher pathMatcher = new AntPathMatcher();
private SysUserService sysUserService;

public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}

//认证token
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

String token = request.getHeader("Authorization");
if (token != null) {
//验证token,并且写入权限

Map map = TokenService.parseToken(token);
String username = (String) map.get(TokenService.userKey);
if (username != null) {

//无法自动注入,只能手工注入,不知道为啥
sysUserService= BeanFactoryUtil.getBean(SysUserService.class);
SysUser sysUser = sysUserService.getAll(username);

// 这里直接注入角色,因为JWT已经验证了用户合法性,所以principal和credentials直接为null即可
List<GrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority(sysUser.getRolename()));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(sysUser,
null, list);

// 如果验证失败,设置异常;否则将UsernamePasswordAuthenticationToken注入到框架中
if (authentication == null) {
//手动设置异常
request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION",
new AuthenticationCredentialsNotFoundException("权限认证失败"));
// 转发到错误Url
request.getRequestDispatcher("/login/error").forward(request, response);
} else {
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
filterChain.doFilter(request, response);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * 手工获取注入对象
 */
 
@Component
public class BeanFactoryUtil implements ApplicationContextAware {
    private static ApplicationContext context = null;
 
    public static <T> T getBean(Class<T> type) {
        return context.getBean(type);
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (BeanFactoryUtil.context == null) {
            BeanFactoryUtil.context = applicationContext;
        }
    }
}

  

 

14、springboot 接口时间映射处理,如果不处理会提示 bad request,"Bad Request",时间处理配置,此时就不用转换date了,直接映射成date

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
*  增加时间处理格式,否则接口无法映射时间,会报错
* */
@Configuration
public class WebMvcConfigurerIMP implements WebMvcConfigurer {
    /**
     * 自动转换时间格式
     *
     * @param registry date
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
    }
}

15、用户密码是增加加密方式,注册的时候也需要加密方式,加密方式配置,配置文件配置,注册的时候密码也需要增加加密

1
2
//使用加密方式
       auth.userDetailsService(userDetailimpl).passwordEncoder(bCryptPasswordEncoder());
1
2
3
4
5
6
7
8
9
@PostMapping("/insertsysuser")
  public int insertSysUser(SysUser sysUser) throws ParseException {
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   //  Date date =sdf.parse("2021-1-1 09:35:00");
     BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
     sysUser.setPassword( passwordEncoder.encode(sysUser.getPassword()));
   //  sysUser.setCreatetime(date);
     return sysUserService.insertSysUser(sysUser);
  }

 

16、Springboot 登录认证失败处理类,该类主要用于验证失败返回错误信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;
 
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException
    {
        int code = HttpStatus.UNAUTHORIZED;
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

  

posted on   topguntopgun  阅读(414)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
历史上的今天:
2020-11-02 ABP 生成Application.xml 文件无法复制到Host目录下面

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示