写点什么

SpringBoot 整合 Shiro 实现权限管理,rabbitmq 原理图

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:5964 字

    阅读完需:约 20 分钟

密码加密加盐


====================================================================


用户密码一般不会以明文方式保存,这样无法保证安全性,所以一般都需要加密。


SimpleHash 类可以实现基本的加密,几种创建方式:


new SimpleHash("加密算法","原始密码")


new SimpleHash("加密算法","原始密码",盐)


new SimpleHash("加密算法","原始密码",盐,迭代次数)


参数说明:


加密算法一般使用常用的 md5 算法


盐的作用是提高密码安全性,如两个用户的原始密码都是 123,则加密后的密文都是相同的,如果破解了一个用户的密码,另一个用户的密码也一同破解了,如果给密码加盐,每个用户的盐不同,加密后密码就都会不同,增加了破解难度。


迭代次数是加密一次后,再对密文再次加密,也能提高安全性。


下面我们以 md5 算法对“123456”加密,盐是“007”,迭代次数为 10。


SimpleHash md5 = new SimpleHash("md5", "123456",ByteSource.Util.bytes("007"), 10);


System.out.println(md5);


输出:44202d045439dc33a2e43d2828d08e19


修改 MyRealm 的 doGetAuthenticationInfo 方法,这里将密文和盐直接写在代码中,实际应用时密文和盐是通过用户名从数据库中查询出来的。


//返回验证信息,参数:1、用户名 2、正确密码 3、盐 4、realm 名称


return new SimpleAuthenticationInfo(username,"44202d045439dc33a2e43d2828d08e19", ByteSource.Util.bytes("007"),getName());


需要给自定义 Realm 添加密码匹配器


//创建默认安全管理器


DefaultSecurityManager securityManager = new DefaultSecurityManager();


//安全管理器配置自定义 Realm


MyRealm realm = new MyRealm();


//创建密码匹配器


HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");


//设置迭代次数


md5.setHashIterations(10);


//配置匹配器


realm.setCredentialsMatcher(md5);


securityManager.setRealm(realm);


//SecurityUtils 配置安全管理器


SecurityUtils.setSecurityManager(securityManager);


//获得 Subject 对象


Subject subject = SecurityUtils.getSubject();


//创建账号密码 token


UsernamePasswordToken user = new UsernamePasswordToken("zhang", "123456");


//登录验证


subject.login(user);


//权限判断


System.out.println("是否登录成功:" + subject.isAuthenticated());


System.out.println("是否拥有 role1 角色:" + subject.hasRole("role1"));


System.out.println("是否拥有 delete 权限:" + subject.isPermitted("user:delete"));



SpringBoot+MyBatis+Shiro 整合


========================================================================================


1、表设计


![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS3cRTkB-1608710040674)(shiro.assets/1608618965879.png)]](https://img-blog.csdnimg.cn/20201223155947307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMzNDMxMTQ=,size_16,color_FFFFFF,t_


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


70#pic_center)


  • s_user 用户表

  • s_role 角色表

  • s_menu 菜单表(权限表)

  • s_user_menu 用户角色中间表

  • s_role_menu 角色菜单中间表


2、添加依赖


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-web</artifactId>


</dependency>


<dependency>


<groupId>org.projectlombok</groupId>


<artifactId>lombok</artifactId>


<optional>true</optional>


</dependency>


<dependency>


<groupId>com.baomidou</groupId>


<artifactId>mybatis-plus-boot-starter</artifactId>


<version>3.3.2</version>


</dependency>


<dependency>


<groupId>mysql</groupId>


<artifactId>mysql-connector-java</artifactId>


<version>8.0.16</version>


</dependency>


<dependency>


<groupId>org.apache.shiro</groupId>


<artifactId>shiro-spring-boot-web-starter</artifactId>


<version>1.7.0</version>


</dependency>


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-thymeleaf</artifactId>


</dependency>


<dependency>


<groupId>com.github.theborakompanioni</groupId>


<artifactId>thymeleaf-extras-shiro</artifactId>


<version>2.0.0</version>


</dependency>


2、SpringBoot 配置

jdbc 配置

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


spring.datasource.url=jdbc:mysql://localhost:3306/erp_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8


spring.datasource.username=root


spring.datasource.password=123456

mybatis 配置

mybatis-plus.type-aliases-package=com.blb.blb_erp.entity


mybatis-plus.mapper-locations=classpath:mapper/*.xml

shiro 配置

登录页面

shiro.loginUrl=/pages/login.html

登录失败跳转页面

shiro.unauthorizedUrl=/pages/failed.html

登录成功跳转页面

shiro.successUrl=/pages/index.html


3、编写 Mapper 接口


需要三个方法:


  1. 按用户名查找用户

  2. 按用户 id 查询所有菜单

  3. 按用户 id 查询所有角色


/**


  • 用户接口


*/


public interface SUserMapper extends BaseMapper<SMenu>{


/**


  • 通过用户名查询用户

  • @param username

  • @return


*/


SUser selectUserByUsername(String username);


}


映射文件:


<?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.blb.blb_erp.mapper.SUserMapper">


<select id="selectUserByUsername" resultType="SUser">


select * from s_user where user_name = #{username}


</select>


</mapper>


/**


  • 菜单接口


*/


public interface SMenuMapper extends BaseMapper<SMenu>{


/**


  • 根据 userId 查询所有权限

  • @param userId

  • @return


*/


List<SMenu> selectMenusByUserId(String userId);


}


映射文件:


<?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.blb.blb_erp.mapper.SMenuMapper">


<select id="selectMenusByUserId" resultType="SMenu">


select m.* from s_user u,s_role r,s_user_role ur,s_menu m,s_role_menu rm


where ur.role_id = r.id and ur.user_id = u.id and rm.role_id = r.id and rm.menu_id = m.id


and u.id = #{userId}


</select>


</mapper>


/**


  • 角色接口


*/


public interface SRoleMapper extends BaseMapper<SMenu>{


/**


  • 根据用户 id 查询所有角色

  • @param userId

  • @return


*/


List<SRole> selectRolesByUserId(String userId);


}


映射文件:


<?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.blb.blb_erp.mapper.SRoleMapper">


<select id="selectRolesByUserId" resultType="SRole">


select r.* from s_user_role ur


join s_user u on ur.user_id = u.id


join s_role r on ur.role_id = r.id


where ur.user_id = #{userId}


</select>


</mapper>


4、自定义 Realm


/**


  • 用户 Realm


*/


public class UserRealm extends AuthorizingRealm {


@Autowired


private SUserMapper userMapper;


@Autowired


private SRoleMapper roleMapper;


@Autowired


private SMenuMapper menuMapper;


@Override


protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {


//获得用户对象


SUser user = (SUser) principalCollection.getPrimaryPrincipal();


//查询权限和角色


List<SMenu> menus = menuMapper.selectMenusByUserId(user.getId());


List<SRole> roles = roleMapper.selectRolesByUserId(user.getId());


//保存权限和角色名称的集合


List<String> strRoles = new ArrayList<>();


roles.forEach(r -> strRoles.add(r.getRoleName()));


List<String> strMenus = new ArrayList<>();


menus.forEach(m -> strMenus.add(m.getMenuName()));


//返回带有角色和权限名称的授权信息


SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();


info.addRoles(strRoles);


info.addStringPermissions(strMenus);


return info;


}


@Override


protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {


//获得账号


String username = authenticationToken.getPrincipal().toString();


//通过账号查询用户


SUser user = userMapper.selectUserByUsername(username);


if(user == null){


throw new UnknownAccountException("此用户不存在");


}


//返回验证信息 参数:1、用户对象 2、正确密码 3、盐 4、realm 名称


return new SimpleAuthenticationInfo(user,user.getPassWord(), ByteSource.Util.bytes(user.getSalt()),getName());


}


}


5、Shiro 配置类


/**


  • Shiro 配置


*/


@Configuration


public class ShiroConfig {


//返回 Realm


@Bean


public UserRealm myRealm(){


UserRealm myRealm = new UserRealm();


//设置密码匹配器


HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");


md5.setHashIterations(10);


myRealm.setCredentialsMatcher(md5);


//关闭缓存


myRealm.setCachingEnabled(false);


return myRealm;


}


//返回面向 Web 开发的安全管理器


@Bean


public DefaultWebSecurityManager defaultWebSecurityManager(){


DefaultWebSecurityManager sm = new DefaultWebSecurityManager();


//设置自定义 Realm


sm.setRealm(myRealm());


return sm;


}


//返回 Shiro 过滤器链定义


@Bean


public ShiroFilterChainDefinition shiroFilterChainDefinition() {


DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();


//定义过滤器链 key 为 url,value 为 anon 不验证,authc 验证


//anon 在前,authc 在后,需要使用 LinkedHashMap 保留顺序


LinkedHashMap<String, String> map = new LinkedHashMap<>();


map.put("/pages/login.html","anon");


map.put("/user/login","anon");


map.put("/**","authc");


chainDefinition.addPathDefinitions(map);


return chainDefinition;


}


//启动 thymeleaf 的 shiro 标签


@Bean


public ShiroDialect shiroDialect(){


return new ShiroDialect();


}


}


6、启动类


@MapperScan("com.blb.blb_erp.mapper")


@SpringBootApplication


public class BlbErpApplication {


public static void main(String[] args) {


SpringApplication.run(BlbErpApplication.class, args);


}


}


7、控制器


@Data


@AllArgsConstructor


@NoArgsConstructor


public class JsonResult {


private int code;


private Object data;


}


@RestController


@RequestMapping("/user")


public class UserController {


@PostMapping("/login")


public JsonResult login(String username,String password){


//创建 Token


UsernamePasswordToken token = new UsernamePasswordToken(username, password);


//获得 subject


Subject subject = SecurityUtils.getSubject();


try {


subject.login(token);


return new JsonResult(1,"登录成功");


}catch (AuthenticationException ex){


ex.printStackTrace();


}


return new JsonResult(0,"账号或密码错误");


}


@RequiresRoles("管理员")


@GetMapping("/role-admin")


public String testRole(){


return "有管理员角色";


}


@RequiresPermissions("部门管理")


@GetMapping("/menu-dept")


public String testMenu(){


return "有部门管理权限";


}


}


@RequiresRoles、@RequiresPermissions 写在控制器的方法上,登录用户有对应的角色和权限才能访问。


RememberMe


========================================================================


可以在登录页面上添加记住我功能,勾选后下次不用登录直接进去系统了


1、页面上添加 RememberMe 复选框


<!DOCTYPE html>


<html lang="en" xmlns:th="http://www.thymeleaf.org">


<head>


<meta charset="UTF-8">


<title>登录</title>


<link rel="stylesheet" href="/elementui/index.css">


<style>


.box-card{


margin:200px auto;


width: 480px;


}


.clearfix{


text-align: center;


color:#303133;


font-size: 18px;


}


.login-form{


width: 400px;


}


</style>


</head>


<body>


<div id="app">


<el-card class="box-card">


<div slot="header" class="clearfix">


<span>系统登录</span>


</div>


<el-form class="login-form" ref="form" :model="form" label-width="80px">


<el-form-item label="账号">


<el-input v-model="form.username"></el-input>


</el-form-item>


<el-form-item label="密码">


<el-input type="password" v-model="form.password"></el-input>


</el-form-item>


<el-form-item >


<el-checkbox v-model="form.rememberMe">记住我</el-checkbox>


</el-form-item>


<el-form-item>


<el-button type="primary" @click="login">登 录</el-button>


<el-button>取 消</el-button>


</el-form-item>


</el-form>


</el-card>


</div>


<script src="/vue/vue.js"></script>


<script src="/elementui/index.js"></script>


<script src="/axios/axios.min.js"></script>


<script src="/qs/qs.min.js"></script>


<script>


new Vue({


el:"#app",


data:{


form:{username:"",password:"",rememberMe:false}


},


methods:{


login:function () {


//Qs.stringify(this.form) 将 form 由{xx:值} 转为 xx=值 &xx=值


axios.post("/user/login",Qs.stringify(this.form))


.then(res=>{


if(res.data.code == 1){


location.href = "/pages/index.html";


}


});


}


}


});


</script>


</body>


</html>


2、修改登录方法


@PostMapping("/login")


public JsonResult login(String username,String password,Boolean rememberMe){


//创建 Token


UsernamePasswordToken token = new UsernamePasswordToken(username, password);


//设置记住我


token.setRememberMe(rememberMe);


//获得 subject


Subject subject = SecurityUtils.getSubject();


try {


subject.login(token);


return new JsonResult(1,"登录成功");


}catch (AuthenticationException ex){


ex.printStackTrace();


}


return new JsonResult(0,"账号或密码错误");


}


3、修改 Shiro 配置类


//创建 RememberMe 管理器


public CookieRememberMeManager rememberMeManager(){


CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();


SimpleCookie rememberMe = new SimpleCookie("rememberMe");


//单位是秒 过期时间


rememberMe.setMaxAge(60 * 10);


rememberMeManager.setCookie(rememberMe);


return rememberMeManager;


}


//返回面向 Web 开发的安全管理器


@Bean

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
SpringBoot整合Shiro实现权限管理,rabbitmq原理图