本文提供相关源码,请放心食用,详见网页侧边栏或底部,有疑问请评论或 Issue
Shiro 是在 Java Web 开发中,比较常见的安全框架技术,在本文章,将介绍如何在 SpringBoot 中去使用 Shiro。在本篇文章中,使用的技术为:SpringBoot 2.0、Shiro 和 MyBatis-Plus,下面就跟着我来一步步实践吧。
一、导入依赖
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
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version> </dependency>
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatisplus-spring-boot-starter</artifactId> <version>1.0.5</version> </dependency>
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>2.1.9</version> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
|
二、自定义 ShiroRealm
继承 AuthorizingRealm
类,并重写用于认证的 doGetAuthenticationInfo
和用于授权的 doGetAuthorizationInfo
,如果你在 SSM 中使用过 Shiro,就很简单了。
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
|
public class ShiroRealm extends AuthorizingRealm {
@Autowired IUserService userService;
@Autowired IUserRoleService userRoleService;
@Autowired IRoleService roleService;
@Autowired IPermissionService permissionService;
@Autowired IRolePermissionService rolePermissionService;
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { if(token.getPrincipal() == null) { return null; }
String name = token.getPrincipal().toString(); String password = new String((char[])token.getCredentials());
User user = userService.findByName(name);
if(user == null || !password.equals(user.getPassword())) { throw new IncorrectCredentialsException("登录失败,用户名或密码错误"); }
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name,password, getName());
return simpleAuthenticationInfo; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String name = (String)principalCollection.getPrimaryPrincipal(); User user = userService.findByName(name);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
List<Role> roles = getRoles(user.getId());
for(Role role : roles) { simpleAuthorizationInfo.addRole(role.getName());
List<Permission> permissions = getPermission(role.getId()); for(Permission permission : permissions) { simpleAuthorizationInfo.addStringPermission(permission.getName()); } } return simpleAuthorizationInfo; }
private List<Role> getRoles(String userId) { List<UserRole> userRoles = userRoleService.selectList( new EntityWrapper<UserRole>() .eq("user_id", userId)); List<Role> list = new ArrayList<>();
for(UserRole userRole : userRoles) { Role role = roleService.selectById(userRole.getRoleId()); list.add(role); } return list; }
private List<Permission> getPermission(String roleId) { List<RolePermission> rolePermissions = rolePermissionService.selectList( new EntityWrapper<RolePermission>() .eq("role_id", roleId));
List<Permission> list = new ArrayList<>();
for(RolePermission rolePermission : rolePermissions) { Permission permission = permissionService.selectById(rolePermission.getPermissionId()); list.add(permission); } return list; } }
|
三、设置 ShiroConfig
SpringBoot 相较于 SSM,将配置使用注解形式引入,那么原本写入 xml 的 Shrio 配置文件,就可以用注解注入了:
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
|
@Configuration public class ShiroConfig {
@Bean public ShiroRealm shiroRealm() { return new ShiroRealm(); }
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(shiroRealm()); return manager; }
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setLoginUrl("/login");
Map<String,String> map = new HashMap<>(16); map.put("/", "anon"); map.put("/logout", "logout"); map.put("/**", "authc");
bean.setFilterChainDefinitionMap(map); return bean; }
@Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager manager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager);
return advisor; } }
|
四、编写 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
| @Controller public class LoginController { @Autowired IUserService userService;
@GetMapping("/") public String index() { return "index.html"; }
@GetMapping("/login") public String login() { return "login.html"; }
@PostMapping("/login") public String login(User user) {
Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken( user.getName(), user.getPassword()); subject.login(usernamePasswordToken);
return "redirect:/home"; }
@GetMapping("/home") public String home() { return "home.html"; }
@RequestMapping("/logout") public String logout() { return "redirect:/logout"; }
@RequiresRoles("admin") @RequiresPermissions("create") @RequestMapping("/adminCreate") @ResponseBody public String adminCreate() { return "只有[admin]身份且具有[create]权限才能看见这句话"; } }
|
五、测试程序
我的数据库拥有以下身份和权限:
Role |
Permissions |
admin |
create、update、delete、select |
teacher |
create、select |
student |
select |
拥有以下几位用户:
User |
Role |
jitwxs |
admin |
zhangsan |
student |
当我测试该方法时:
1 2 3 4 5 6 7
| @RequiresRoles("admin") @RequiresPermissions("update") @RequestMapping("/adminCreate") @ResponseBody public String adminCreate() { return "只有[admin]身份且具有[create]权限才能看见这句话"; }
|
六、番外
在这个例子中,踩了几个坑,罗列一下:
(1)ShiroRealm 中无法注入 Service
解决办法:《Shiro 解决无法注入 Service 问题》
(2)ShiroRealm 的授权方法 doGetAuthorizationInfo()
没有调用
解决办法:缺少 AOP,导入 AOP 包
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|