security基于表单提交使用

7/26/2022 安全框架

# 前述

本文对应的源代码已经上传到 Gitee (opens new window),对应项目为 simple ,登录方式为表单登录

后续会持续记录基于 JWT微服务 等场景的实例代码。

# 基于表单登录的实现

# 一、引入依赖

这一块我们直接跳到引入依赖,像新建项目这种不再描述

<!-- SpringBoot的版本 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.2</version>
    <relativePath/> 
</parent>

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--模板引擎-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!--对Thymeleaf添加Spring Security标签支持-->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
</dependencies>
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

# 二、其他配置

  • mybatis-plus 以及一些常量工具类的配置描述就不再展开,查看源码即可。

# 三、自定义security处理器

主要有以下几个

  • 认证失败的处理类
  • 登录成功处理类
  • 自定义并发会话失效策略
  • 退出处理器

认证失败处理类

/**
 * @version 1.0.0
 * @className: AuthenticationEntryPointImpl
 * @description: 认证失败的处理类
 * @author: LiJunYi
 * @create: 2022/7/25 17:39
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
    {
        int code = HttpStatus.UNAUTHORIZED;
        String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

登录成功处理类

/**
 * @version 1.0.0
 * @className: CustomAuthenticationSuccessImpl
 * @description: 登录成功处理类
 * @author: LiJunYi
 * @create: 2022/7/26 13:30
 */
@Component
public class CustomAuthenticationSuccessImpl implements AuthenticationSuccessHandler
{

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication e) {
        String msg = "登录成功";
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(msg)));
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

并发登录会话失效处理类

/**
 * @version 1.0.0
 * @className: CustomExpiredSessionStrategyImpl
 * @description: 自定义并发会话失效策略
 * @author: LiJunYi
 * @create: 2022/7/26 11:53
 */
@Component
public class CustomExpiredSessionStrategyImpl implements SessionInformationExpiredStrategy , Serializable
{

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException
    {
        int code = HttpStatus.CONFLICT;
        String msg = "您的账号已经在其它设备登录,如果非本人操作,请立即修改密码!";
        HttpServletResponse response = event.getResponse();
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

退出处理类

/**
 * @version 1.0.0
 * @className: LogoutSuccessHandlerImpl
 * @description: 退出处理器
 * @author: LiJunYi
 * @create: 2022/7/26 8:34
 */
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication e) throws IOException {
        boolean isAjaxRequest = ServletUtils.isAjaxRequest(request);
        if (isAjaxRequest)
        {
            ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
        }else
        {
            response.sendRedirect("/login");
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 四、SecurityConfig

/**
 * @version 1.0.0
 * @className: SecurityConfig
 * @description: SpringSecurity 5.7.x新用法配置
 * @author: LiJunYi
 * @create: 2022/7/26 8:43
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig
{
    /**
     * 自定义用户登录处理逻辑
     */
    private final MyUserDetailsServiceImpl userDetailsService;

    /**
     * 登录成功处理
     */
    private final CustomAuthenticationSuccessImpl customAuthenticationSuccess;

    /**
     * 注销成功后处理
     */
    private final LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * 未经授权处理程序
     */
    private final AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 并发登录控制处理
     */
    private final CustomExpiredSessionStrategyImpl expiredSessionStrategy;

    /**
     * 构造函数注入
     *
     * @param userDetailsService          用户详细信息服务
     * @param logoutSuccessHandler        注销成功处理程序
     * @param unauthorizedHandler         未经授权处理程序
     * @param expiredSessionStrategy      过期会话策略
     * @param customAuthenticationSuccess 自定义身份验证成功
     */
    @Autowired
    public SecurityConfig(MyUserDetailsServiceImpl userDetailsService,LogoutSuccessHandlerImpl logoutSuccessHandler,
                          AuthenticationEntryPointImpl unauthorizedHandler,
                          CustomExpiredSessionStrategyImpl expiredSessionStrategy,
                          CustomAuthenticationSuccessImpl customAuthenticationSuccess) {
        this.userDetailsService = userDetailsService;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.unauthorizedHandler = unauthorizedHandler;
        this.expiredSessionStrategy = expiredSessionStrategy;
        this.customAuthenticationSuccess = customAuthenticationSuccess;
    }


    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        AuthenticationManager authenticationManager = configuration.getAuthenticationManager();
        return configuration.getAuthenticationManager();
    }

    /**
     * 配置加密方式
     *
     * @return {@link PasswordEncoder}
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 侦听器配置,使 Spring Security 更新有关会话生命周期事件的信息
     * 并发会话控制:https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html
     * @return {@link HttpSessionEventPublisher}
     */
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    /**
     * 过滤器链
     *
     * @param http http
     * @return {@link SecurityFilterChain}
     * @throws Exception 异常
     */
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception
    {
        http.authorizeRequests(authorize ->
                authorize.mvcMatchers("/login","/userLogin","/noPermission","/static/css/**","/static/util/javascript/**").permitAll()
                        .anyRequest().authenticated()
        )
                .csrf().disable()
                .formLogin(form -> form
                        .loginPage("/login")
                        .loginProcessingUrl("/userLogin")
                        .successHandler(customAuthenticationSuccess)
                        .permitAll());
        // 退出过滤器
        http.logout(logout -> logout
                .deleteCookies("JSESSIONID")
                .logoutSuccessHandler(logoutSuccessHandler));
        // 认证失败处理类
        http.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).accessDeniedPage("/noPermission");
        // 并发会话控制
        http.sessionManagement(session ->  session
                .maximumSessions(1)
                .expiredSessionStrategy(expiredSessionStrategy));
        http.userDetailsService(userDetailsService);
        return http.build();
    }
}

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

# 五、自定义Model实现UserDetails

/**
 * @version 1.0.0
 * @className: LoginUser
 * @description: 自定义用户对象并实现UserDetails
 * @author: LiJunYi
 * @create: 2022/7/26 12:20
 */
public class LoginUser implements UserDetails
{
    private static final long serialVersionUID = 1L;

    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 部门ID
     */
    private Long deptId;

    /**
     * 登录时间
     */
    private Long loginTime;

    /**
     * 权限列表
     */
    private Set<String> permissions;

    /**
     * 用户信息
     */
    private SysUser user;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Long getDeptId() {
        return deptId;
    }

    public void setDeptId(Long deptId) {
        this.deptId = deptId;
    }

    public Long getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(Long loginTime) {
        this.loginTime = loginTime;
    }

    public Set<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }

    public SysUser getUser() {
        return user;
    }

    public void setUser(SysUser user) {
        this.user = user;
    }

    public LoginUser()
    {
    }

    public LoginUser(SysUser user, Set<String> permissions)
    {
        this.user = user;
        this.permissions = permissions;
    }

    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptId = deptId;
        this.user = user;
        this.permissions = permissions;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @JSONField(serialize = false)
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

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

    /**
     * 账户是否未过期,过期无法验证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 是否可用 ,禁用的用户不能身份验证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isEnabled() {
        return true;
    }
}

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

# 六、自定义实现UserDetailsService

/**
 * @version 1.0.0
 * @className: MyUserDetailsServiceImpl
 * @description: security具体登录逻辑
 * @author: LiJunYi
 * @create: 2022/7/26 9:11
 */
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService
{
    private static final Logger log = LoggerFactory.getLogger(MyUserDetailsServiceImpl.class);

    private final ISysUserService userService;

    private final SysPermissionService permissionService;

    /**
     * 构造器注入
     *
     * @param userService       用户服务
     * @param permissionService 许可服务
     */
    public MyUserDetailsServiceImpl(ISysUserService userService, SysPermissionService permissionService)
    {
        this.userService = userService;
        this.permissionService = permissionService;
    }


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        SysUser user = userService.getByUsername(username);
        if (ObjectUtil.isNull(user))
        {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username + " 不存在");
        }
        // 获取角色
        List<SysRole> roles = permissionService.getRolesByUserId(user);
        user.setRoles(roles);
        return createLoginUser(user);
    }

    public UserDetails createLoginUser(SysUser user)
    {
        return new LoginUser(Convert.toLong(user.getId()), 1000L, user, permissionService.getMenuPermission(user));
    }

}
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

# 七、获取登录用户权限

/**
 * @version 1.0.0
 * @className: SysPermissionService
 * @description:
 * @author: LiJunYi
 * @create: 2022/7/26 12:36
 */
@Component
public class SysPermissionService
{
    private final ISysRoleService roleService;
    private final ISysResourceService resourceService;

    /**
     * 构造器注入
     *
     * @param roleService     角色服务
     * @param resourceService 资源服务
     */
    public SysPermissionService(ISysRoleService roleService, ISysResourceService resourceService) {
        this.roleService = roleService;
        this.resourceService = resourceService;
    }

    /**
     * 获取角色数据权限
     *
     * @param user 用户信息
     * @return 角色权限信息
     */
    public Set<String> getRolePermission(SysUser user)
    {
       return roleService.getRolePermissionByUserId(user.getId());
    }

    /**
     * 获取菜单数据权限
     *
     * @param user 用户信息
     * @return 菜单权限信息
     */
    public Set<String> getMenuPermission(SysUser user)
    {
        List<SysResource> resources = resourceService.listResourceByUserId(user.getId());
        List<String> perms = resources.stream().map(SysResource::getPermission).collect(Collectors.toList());
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms)
        {
            if (StrUtil.isNotEmpty(perm))
            {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 获取角色通过用户id
     *
     * @param user 用户
     * @return {@link List}<{@link SysRole}>
     */
    public List<SysRole> getRolesByUserId(SysUser user)
    {
        return roleService.getRolesByUserId(user.getId());
    }
}

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

# 八、自定义权限校验注解

/**
 * @version 1.0.0
 * @className: PermissionService
 * @description: 自定义的权限校验,代码来源(RuoYi-Vue)项目
 * @author: LiJunYi
 * @create: 2022/7/26 15:38
 */
@Service("ss")
public class PermissionService
{
    /** 所有权限标识 */
    private static final String ALL_PERMISSION = "*:*:*";

    /** 管理员角色权限标识 */
    private static final String SUPER_ADMIN = "admin";

    private static final String ROLE_DELIMETER = ",";

    private static final String PERMISSION_DELIMETER = ",";

    /**
     * 验证用户是否具备某权限
     *
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermi(String permission)
    {
        if (StrUtil.isEmpty(permission))
        {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtil.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
        return hasPermissions(loginUser.getPermissions(), permission);
    }

    /**
     * 验证用户是否不具备某权限,与 hasPermi逻辑相反
     *
     * @param permission 权限字符串
     * @return 用户是否不具备某权限
     */
    public boolean lacksPermi(String permission)
    {
        return hasPermi(permission) != true;
    }

    /**
     * 验证用户是否具有以下任意一个权限
     *
     * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
     * @return 用户是否具有以下任意一个权限
     */
    public boolean hasAnyPermi(String permissions)
    {
        if (StrUtil.isEmpty(permissions))
        {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtil.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
        Set<String> authorities = loginUser.getPermissions();
        for (String permission : permissions.split(PERMISSION_DELIMETER))
        {
            if (permission != null && hasPermissions(authorities, permission))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断用户是否拥有某个角色
     *
     * @param role 角色字符串
     * @return 用户是否具备某角色
     */
    public boolean hasRole(String role)
    {
        if (StrUtil.isEmpty(role))
        {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtil.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
        for (SysRole sysRole : loginUser.getUser().getRoles())
        {
            String roleKey = sysRole.getRoleKey();
            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StrUtil.trim(role)))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 验证用户是否不具备某角色,与 isRole逻辑相反。
     *
     * @param role 角色名称
     * @return 用户是否不具备某角色
     */
    public boolean lacksRole(String role)
    {
        return !hasRole(role);
    }

    /**
     * 验证用户是否具有以下任意一个角色
     *
     * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
     * @return 用户是否具有以下任意一个角色
     */
    public boolean hasAnyRoles(String roles)
    {
        if (StrUtil.isEmpty(roles))
        {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtil.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
        for (String role : roles.split(ROLE_DELIMETER))
        {
            if (hasRole(role))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否包含权限
     *
     * @param permissions 权限列表
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Set<String> permissions, String permission)
    {
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StrUtil.trim(permission));
    }
}
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

# 九、登录接口以及业务代码

/**
 * 登录控制器
 *
 * @author LiJunYi
 * @date 2022/07/26
 */
@Controller
public class LoginController
{
    /**
     * 身份验证管理器
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 登录页面
     *
     * @return {@link String}
     */
    @GetMapping("login")
    public String login() {
        return "login";
    }

    /**
     * 登录方法
     *
     * @param user 用户
     * @return {@link AjaxResult}
     */
    @PostMapping("userLogin")
    @ResponseBody
    public AjaxResult login(SysUser user)
    {
        if (StrUtil.isEmpty(user.getUsername()) || StrUtil.isEmpty(user.getPassword()))
        {
            return AjaxResult.error("用户名或密码未输入!");
        }
        // 用户验证
        Authentication authentication = null;
        try {
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword()));
        }catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                throw new ServiceException("密码错误");
            }
            else
            {
                throw new ServiceException(e.getMessage());
            }
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return AjaxResult.success();
    }

    /**
     * 首页
     *
     * @return {@link String}
     */
    @GetMapping("index")
    public String indexPage()
    {
        return "index";
    }

    /**
     * 无权限页面
     *
     * @return {@link String}
     */
    @PostMapping("noPermission")
    public String noPermissionHtml()
    {
        return "noPermission";
    }
}

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
79
80
81
/**
 * <p>
 * 系统用户 前端控制器
 * </p>
 *
 * @author LiJunYi
 * @since 2020-05-25
 */
@Controller
@RequestMapping("/sys/user")
public class SysUserController {
    @Resource
    private ISysUserService userService;

    @PreAuthorize("@ss.hasPermi('sys:user:view')")
    @GetMapping("list")
    public String list(Model model) {
        model.addAttribute("userList", userService.list());
        LoginUser user = SecurityUtils.getLoginUser();
        System.out.println("SecurityUtils.getLoginUser===" + user);
        return "user_list";
    }

    @GetMapping("/add")
    @ResponseBody
    @PreAuthorize("@ss.hasPermi('sys:user:add')")
    public String add() {
        return "跳转新增页面成功";
    }

    @GetMapping("/edit/{id}")
    @PreAuthorize("@ss.hasPermi('sys:user:edit')")
    @ResponseBody
    public String edit(@PathVariable Long id) {
        return "跳转修改页面成功";
    }

    @GetMapping("/del/{id}")
    @PreAuthorize("@ss.hasPermi('sys:user:del')")
    @ResponseBody
    public String del(@PathVariable Long id) {
        return "删除成功";
    }
}
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

# 十、测试示例图

SpringSecurity

SpringSecurity

SpringSecurity

SpringSecurity

SpringSecurity

以上就是 SpringBoot 接入 SpringSecurity (v5.7.x) 的步骤。

# 唠嗑

最开始接入的时候,问题还挺多,特别是 securityConfig 配置类这块,虽说变化不大,但是因为官方文档内容比较散,有些配置冲突或者多余也没有标记,还是得自己一步步踩坑过去才知道哪些才是对的配置。

项目源码中,PermissionService 类来源于 RuoYi-Vue 项目。当然你也可以自己实现权限的校验,毕竟登录的时候,权限已经绑定在 loginUser 对象中,只要判断是否包含注解带入的参数即可。

后面一章节将整合jwt,也就是自定义登录过滤器来实现登录,文中有错误的地方希望多多指正。