posts - 609,  comments - 13,  views - 64万
< 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

SpringBoot整合SpringSecurity:https://www.bilibili.com/video/BV1KE411i7bC?p=1
SpringBoot整合Shiro框架:https://www.bilibili.com/video/BV1NE411i7S8?p=1
SpringBoot文档:http://felord.cn/_doc/_springboot/2.1.5.RELEASE/_book/
https://docs.spring.io/spring-boot/docs/current/reference/html/
系统安全框架,用于认证、授权 
1、SpringSecurity依赖

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

2、命名空间xmlns:th=http://www.thymeleaf.org   xmlns:sec=http://www.thymeleaf.org/extras/spring-security   xmlns:shiro=http://www.pollix.at/thymeleaf/shiro

3、自己创建SecurityConfig

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
package com.jay.SpringBootStudy8.config;
 
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import static org.springframework.security.config.Customizer.withDefaults;
 
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        //BCryptPasswordEncoder 加密方式,用户名admin、密码123456,可以and()连接多个
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
                .and()
                .withUser("user1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2")
                ;
    }
    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults());
        //首页/所有人可访问
        //功能页只有具有权限的人才能访问
        http.authorizeRequests()
                .antMatchers("/level1").hasRole("vip1")
                .antMatchers("/level2").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");
 
        //禁用csrf跨站
        http.csrf().disable();
        //开启登录,定制登录页,自定义请求参数名 和 登录请求地址。
        //如果不自定义登录请求地址,那登录form的action需要是loginPage指定的地址 /userLogin
        http.formLogin().loginPage("/userLogin")
                .usernameParameter("uname")
                .passwordParameter("pwd")
                .loginProcessingUrl("/loginHandler");
        //记住我,自定义请求参数
        http.rememberMe().rememberMeParameter("remember");
        //注销、删除cookie、清除session
        http.logout().deleteCookies("remove").invalidateHttpSession(true);
    }
}

 表单userLogin:

1
2
3
4
5
6
7
8
9
<form method="post" th:action="@{/loginHandler}">
username:<input type="text" name="uname" />
<br/>
password:<input type="password" name="pwd" />
<br/>
<input type="checkbox" name="remember" />Remember Me
<br/>
<button type="submit">submit</button>
</form>

 loginHandler可以处理自己其他的登录逻辑。
页面中获取Spring Security登录用户数据:https://www.cnblogs.com/softidea/p/6677665.html     https://blog.csdn.net/cyan20115/article/details/106552758
Spring Security使用jdbc进行权限验证:https://blog.csdn.net/fuzekun/article/details/104344472
Shiro:https://www.jianshu.com/p/7f724bec3dc3
1、引入相关包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
 
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
 
<!--工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.8</version>
</dependency>

2、 自定义ShiroConfig

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
package com.jay.SpringBootStudy8.config;
 
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
 
@Configuration
public class ShiroConfig {
    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        /*
        anon:无需认证即可访问
        authc:必须认证才可访问
        user:必须拥有记住我的功能,才可以用
        perms:必须拥有对某个资源的权限才可以访问
        role:拥有某个角色才可以访问
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        //对所有用户认证,只要登录成功,就可以访问
        //filterMap.put("/sys/*", "authc");
 
        //必须拥有资源权限(推荐),users:mgr 是权限码,存于数据库
//        filterMap.put("/sys/users", "perms[users:mgr]");
        filterMap.put("/sys/roles", "perms[roles:mgr]");
        filterMap.put("/sys/permissions", "perms[permissions:mgr]");
        filterMap.put("/sys/products", "perms[products:mgr]");
        filterMap.put("/sys/articles", "perms[articles:mgr]");
        filterMap.put("/sys/test", "perms[sys:test]");
 
        filterMap.put("/logout", "logout");//登出
        filterMap.put("/loginHandler", "anon");
        bean.setLoginUrl("/login");//登录
        bean.setSuccessUrl("/sys/main");//首页
        bean.setUnauthorizedUrl("/noauth");//错误页面,认证不通过跳转
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }
 
    //权限管理,配置主要是Realm的管理认证
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("customRealm") CustomRealm customRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm);
        return securityManager;
    }
 
    //将自己的验证方式加入容器
    @Bean
    public CustomRealm customRealm() {
        return new CustomRealm();
    }
 
    //集成 thymeleaf-extras-shiro,可以在前端使用shiro语法
    @Bean
    public ShiroDialect getShiroDialect(){return  new ShiroDialect();}
}

shiro异常处理配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.jay.SpringBootStudy8.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.Properties;
@Configuration
public class ShiroExceptionConf {
    @Bean
    public SimpleMappingExceptionResolver resolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/noauth");
        resolver.setExceptionMappings(properties);
        return resolver;
    }
}

 3、自定义CustomRealm

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
package com.jay.SpringBootStudy8.config;
 
import com.jay.SpringBootStudy8.pojo.Permission;
import com.jay.SpringBootStudy8.pojo.Role;
import com.jay.SpringBootStudy8.pojo.SysUser;
import com.jay.SpringBootStudy8.service.PermissionService;
import com.jay.SpringBootStudy8.service.RoleService;
import com.jay.SpringBootStudy8.service.SysUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
 
import java.util.List;
import java.util.stream.Collectors;
 
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;
 
    //授权,装配用户权限
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//        Subject subject = SecurityUtils.getSubject();
//        Session session = subject.getSession();
//        SysUser user = (SysUser) session.getAttribute("user");
 
        //获取登录用户
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        List<Permission> permissions = sysUser.getPermissions();
        List<Role> roles = sysUser.getRoles();
        //添加角色和权限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for (Role role : roles) {
            //添加角色
            info.addRole(role.getName());
        }
        //添加权限
        for (Permission perm : permissions) {
            info.addStringPermission(perm.getCode());
        }
        return info;
    }
 
    //认证,验证用户的账户和密码
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        String username = userToken.getUsername();
        SysUser user = sysUserService.getModelByUserName(username);
        if (user == null) {
            return null;//这里返回后会报出对应异常
        } else {
            List<Role> roles = roleService.getAllByUserId(user.getId());
            user.setRoles(roles);
            List<Integer> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
            List<Permission> perms = permissionService.getAllByRoleIds(roleIds);
            user.setPermissions(perms);
            //盐值加密验证密码
            String realmName = getName();
            ByteSource credentialsSalt = ByteSource.Util.bytes(username);//这里的参数要给个唯一的;
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPwd(), credentialsSalt, realmName);
            // 存入Shiro的Session,这里不是HttpSession,是Shiro独立的Session,
            // 只能通过SecurityUtils.getSubject().getSession()获取
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession();
            session.setAttribute("user", user);
            return info;
        }
    }
}

4、登录Controller

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
package com.jay.SpringBootStudy8.controller;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
 
//在templates目录下的页面只能通过controller来跳转
@Controller
public class IndexController {
    @RequestMapping("/noauth")
    public String unauthorized() {
        return "unauthorized";
    }
 
    @RequestMapping({"/", "/login"})
    public String login() {
        return "login";
    }
 
    @RequestMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }
 
    @PostMapping("/loginHandler")
    public String loginHandler(String name, String pwd, Model model) {
        //MD5加密
        ByteSource credentialsSalt = ByteSource.Util.bytes(name);
        Object obj = new SimpleHash("MD5", pwd, credentialsSalt, 1);
        String pwd2 = obj.toString();
 
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(name, pwd2);
        try {
            //执行登录方法
            subject.login(token);
            return "redirect:/sys/main";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg", "用户名不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }
}

5、 数据库中权限表:permissions

1
2
3
4
5
6
7
Id     Name        Code
--------------------------------------
1   用户管理    users:mgr
2   角色管理    roles:mgr
3   权限管理    permissions:mgr
4   商品管理    products:mgr
5   图文管理    articles:mgr

6、前端需要整合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div th:fragment="header">
    <a th:href="@{/sys/main}">主页</a>
    <span shiro:hasPermission="users:mgr">
        <a th:href="@{/sys/users}">用户管理</a>
    </span>
    <span shiro:hasPermission="roles:mgr">
        <a th:href="@{/sys/roles}">角色管理</a>
    </span>
    <span shiro:hasPermission="permissions:mgr">
        <a th:href="@{/sys/permissions}">权限管理</a>
    </span>
    <span shiro:hasPermission="products:mgr">
        <a th:href="@{/sys/products}">商品管理</a>
    </span>
    <span shiro:hasPermission="articles:mgr">
        <a th:href="@{/sys/articles}">图文管理</a>
    </span>
    <span>
        <a th:href="@{/logout}">注销</a>
    </span>
</div>

引入fragment:<div th:insert="~{common/head :: header}"></div>,shiro前端提示需要引入html命名空间:xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
springboot+shiro实现自定义密码加密及验证(Bcrypt):https://blog.csdn.net/qq_21537671/article/details/107280447
shiro注解:https://blog.csdn.net/qi923701/article/details/75224554/
shiro thymeleaf:https://blog.csdn.net/qq_34579313/article/details/82024058
swagger引用包

1
2
3
4
5
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.10</version>
</dependency>

访问:http://localhost:8091/swagger-ui.html就可以看到所有接口,具体使用方法百度。

posted on   邢帅杰  阅读(193)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示