SpringBoot整合Shiro实现权限管理

//配置安全管理器

SecurityUtils.setSecurityManager(instance);

//获得Subject对象

Subject subject = SecurityUtils.getSubject();

//创建账号密码token

UsernamePasswordToken user = new UsernamePasswordToken(“wang”, “123”);

//登录验证

subject.login(user);

//权限判断

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

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

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

使用wang登录的效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vI0Dd27c-1608710040665)(shiro.assets/1608602846457.png)]

使用zhang登录的效果:

在这里插入图片描述

密码错误会抛出异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhOU7LSv-1608710040670)(shiro.assets/1608602919980.png)]

自定义Realm

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

上面的案例中使用ini文件配置用户、密码、角色和权限等,过于简单,不适合企业级项目的使用,真正项目中用户、角色、权限这些重要数据都保存在数据库中,需要我们通过自己编写的类和方法实现登录和授权。

AuthorizingRealm类实现Realm接口,提供两个方法:

  • doGetAuthenticationInfo 返回登录验证信息,该方法在subject执行login方法后调用

  • doGetAuthorizationInfo 返回用户授权信息,该方法在subject进行权限判断时调用

1) 定义Realm类,此案例为简单起见,账号密码是固定的zhang,123,添加角色role1,添加权限user:select、user:insert、user:delete

/**

  • 用户Realm

*/

public class MyRealm extends AuthorizingRealm {

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

//获得登录用户

String username = (String) principalCollection.getPrimaryPrincipal();

System.out.println(“登录用户授权:” + username);

//授权信息

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//添加角色

info.addRole(“role1”);

//添加权限

info.addStringPermission(“user:select”);

info.addStringPermission(“user:insert”);

info.addStringPermission(“user:delete”);

return info;

}

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

//获得用户输入的账号

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

if(!username.equals(“zhang”)){

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

}

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

return new SimpleAuthenticationInfo(username,“123”, getName());

}

}

2) 使用自定义Realm进行登录和授权

//创建默认安全管理器

DefaultSecurityManager securityManager = new DefaultSecurityManager();

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

securityManager.setRealm(new MyRealm());

//SecurityUtils配置安全管理器

SecurityUtils.setSecurityManager(securityManager);

//获得Subject对象

Subject subject = SecurityUtils.getSubject();

//创建账号密码token

UsernamePasswordToken user = new UsernamePasswordToken(“zhang”, “123”);

//登录验证

subject.login(user);

//权限判断

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

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

密码加密加盐

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

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

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”));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VzpaSypf-1608710040672)(shiro.assets/1608618247076.png)]

SpringBoot+MyBatis+Shiro整合

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

1、表设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS3cRTkB-1608710040674)(shiro.assets/1608618965879.png)]

  • s_user 用户表

  • s_role 角色表

  • s_menu 菜单表(权限表)

  • s_user_menu 用户角色中间表

  • s_role_menu 角色菜单中间表

2、添加依赖

org.springframework.boot

spring-boot-starter-web

org.projectlombok

lombok

true

com.baomidou

mybatis-plus-boot-starter

3.3.2

mysql

mysql-connector-java

8.0.16

org.apache.shiro

shiro-spring-boot-web-starter

1.7.0

org.springframework.boot

spring-boot-starter-thymeleaf

com.github.theborakompanioni

thymeleaf-extras-shiro

2.0.0

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{

/**

  • 通过用户名查询用户

  • @param username

  • @return

*/

SUser selectUserByUsername(String username);

}

映射文件:

<?xml version="1.0" encoding="UTF-8"?>

select * from s_user where user_name = #{username}

/**

  • 菜单接口

*/

public interface SMenuMapper extends BaseMapper{

/**

  • 根据userId查询所有权限

  • @param userId

  • @return

*/

List selectMenusByUserId(String userId);

}

映射文件:

<?xml version="1.0" encoding="UTF-8"?>

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}

/**

  • 角色接口

*/

public interface SRoleMapper extends BaseMapper{

/**

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

  • @param userId

  • @return

*/

List selectRolesByUserId(String userId);

}

映射文件:

<?xml version="1.0" encoding="UTF-8"?>

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}

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 menus = menuMapper.selectMenusByUserId(user.getId());

List roles = roleMapper.selectRolesByUserId(user.getId());

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

List strRoles = new ArrayList<>();

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

List 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复选框

登录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值