Browse Source

update shiro

v-1.5.x
Yangkai.Shen 7 years ago
parent
commit
cadda835b5
19 changed files with 283 additions and 1046 deletions
  1. +4
    -0
      spring-boot-demo-rabc-shiro-mybatis/pom.xml
  2. +8
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/SpringBootDemoRabcShiroMybatisApplication.java
  3. +0
    -30
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/config/DBInitConfig.java
  4. +0
    -78
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/config/ShiroConfig.java
  5. +0
    -124
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/constrant/factory/Constant.java
  6. +0
    -157
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/constrant/factory/ConstantFactory.java
  7. +24
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/domain/LoginParam.java
  8. +0
    -48
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/enums/AclStatusEnum.java
  9. +0
    -48
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/enums/UserStatusEnum.java
  10. +0
    -96
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/MyShiroRealm.java
  11. +0
    -34
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/ShiroUser.java
  12. +0
    -270
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/ShiroUtil.java
  13. +0
    -66
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/factory/Shiro.java
  14. +0
    -95
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/factory/ShiroFactroy.java
  15. +147
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/util/IpUtil.java
  16. +62
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/util/JsonMapper.java
  17. +5
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/resources/application.yml
  18. +2
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/resources/init-sql/shiro.sql
  19. +31
    -0
      spring-boot-demo-rabc-shiro-mybatis/src/main/resources/templates/index.html

+ 4
- 0
spring-boot-demo-rabc-shiro-mybatis/pom.xml View File

@@ -39,6 +39,10 @@
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>


+ 8
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/SpringBootDemoRabcShiroMybatisApplication.java View File

@@ -4,14 +4,22 @@ import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootApplication
@MapperScan(basePackages = {"com.xkcoding.springbootdemorabcshiromybatis.dao"})
@Slf4j
@Controller
public class SpringBootDemoRabcShiroMybatisApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoRabcShiroMybatisApplication.class, args);
log.info("SpringBootDemoRabcShiroMybatisApplication 启动成功。。。。");
}

@GetMapping("/login.page")
public String index() {
return "index";
}
}

+ 0
- 30
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/config/DBInitConfig.java View File

@@ -1,30 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
* <p>
* 数据初始化,实现 CommandLineRunner 接口,启动 springboot 后自动执行,如果有多个这个类可以根据 @Order 来指定执行顺序
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis
* @description: 数据初始化
* @author: yangkai.shen
* @date: Created in 2017/11/29 下午4:32
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Component
@Slf4j
public class DBInitConfig implements CommandLineRunner {

@Override
public void run(String... strings) throws Exception {
log.info("正在初始化数据。。。");
log.info("数据初始化完成。。。");
}

}

+ 0
- 78
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/config/ShiroConfig.java View File

@@ -1,78 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.config;

import com.google.common.collect.Maps;
import com.xkcoding.springbootdemorabcshiromybatis.shiro.MyShiroRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

/**
* Shiro 配置
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.config
* @description: Shiro 配置
* @author: yangkai.shen
* @date: Created in 2017/11/29 下午3:24
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Configuration
@Slf4j
public class ShiroConfig {

@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);

// 设置登录页面,默认是 Web 工程目录下的"/login.jsp"
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置登陆成功后的页面
shiroFilterFactoryBean.setSuccessUrl("/index");
// 设置未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");

// 配置拦截器链,注意配置的顺序,很关键
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
Map<String, String> filterChainDefinitionMap = Maps.newLinkedHashMap();
// 配置可以被匿名访问的地址
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/ajaxLogin", "anon");
// 配置 logout,登出部分逻辑由 shiro 为我们实现
filterChainDefinitionMap.put("/logout", "logout");
// 配置自定义权限
filterChainDefinitionMap.put("/add", "perms[权限添加]");
filterChainDefinitionMap.put("/**", "authc");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
log.info("ShiroFilterFactoryBean 注入成功");

return shiroFilterFactoryBean;
}

@Bean
public SecurityManager securityManager(MyShiroRealm myShiroRealm) {
DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
// 设置 Realm
// 在 shiro 中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。
// 通常情况下,在 Realm 中会直接从我们的数据源中获取 shiro 需要的验证信息。
// 可以说,Realm 是专用于安全框架的 DAO。
defaultSecurityManager.setRealm(myShiroRealm);

return defaultSecurityManager;
}

@Bean
public MyShiroRealm myShiroRealm() {
return new MyShiroRealm();
}
}

+ 0
- 124
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/constrant/factory/Constant.java View File

@@ -1,124 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.constrant.factory;

import java.util.List;

/**
* <p>
* 常量生产工厂的接口
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.constrant.factory
* @description: 常量生产工厂的接口
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午3:51
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
public interface Constant {

String ADMIN_NAME = "超级管理员";

String COMMA = ",";
String SEMI = ";";
String SEPARATE = "|";
String PATH_SEPARATE = "/";
String PERCENT = "%";

/**
* 根据用户id获取用户名称
*
* @param userId 用户id
* @return 用户名称
*/
String getRealNameById(Integer userId);

/**
* 根据用户id获取用户账号
*
* @param userId 用户id
* @return 用户账号
*/
String getUsernameById(Integer userId);

/**
* 根据角色id获取角色名称
*
* @param roleId 角色id
* @return 角色名称
*/
String getRoleName(Integer roleId);

/**
* 根据角色id列表获取角色名称列表
*
* @param roleIds 角色id列表
* @return 角色名称列表
*/
List<String> getRoleNames(List<Integer> roleIds);

/**
* 根据用户id获取角色id列表
*
* @param userId 用户id
* @return 角色id列表
*/
List<Integer> getRoleIds(Integer userId);

/**
* 根据部门id获取部门名称
*
* @param deptId 部门id
* @return 部门名称
*/
String getDeptName(Integer deptId);

/**
* 根据权限id获取权限名称
*
* @param aclId 权限id
* @return 权限名称
*/
String getAclName(Integer aclId);

/**
* 根据权限编号获取权限名称
*
* @param code 权限编号
* @return 权限名称
*/
String getAclNameByCode(String code);

/**
* 根据状态码获取用户登录状态
*
* @param code 状态码
* @return 状态
*/
String getUserStatusName(Integer code);

/**
* 根据状态码获取权限状态
*
* @param code 状态码
* @return 状态
*/
String getAclStatusName(Integer code);

/**
* 获取子部门id
*
* @param deptId 当前部门id
* @return 所有子部门id
*/
List<Integer> getSubDeptId(Integer deptId);

/**
* 获取所有父部门id
*
* @param deptId 当前部门id
* @return 所有父部门id
*/
List<Integer> getParentDeptIds(Integer deptId);

}

+ 0
- 157
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/constrant/factory/ConstantFactory.java View File

@@ -1,157 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.constrant.factory;

import com.google.common.collect.Lists;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xkcoding.springbootdemorabcshiromybatis.dao.*;
import com.xkcoding.springbootdemorabcshiromybatis.enums.AclStatusEnum;
import com.xkcoding.springbootdemorabcshiromybatis.enums.UserStatusEnum;
import com.xkcoding.springbootdemorabcshiromybatis.model.*;
import com.xkcoding.springbootdemorabcshiromybatis.util.SpringContextHolder;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
* <p>
* 常量的生产工厂
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.constrant.factory
* @description: 常量的生产工厂
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午4:01
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Component
@DependsOn("springContextHolder")
public class ConstantFactory implements Constant {

private MybatisShiroAclMapper aclMapper = SpringContextHolder.getBean(MybatisShiroAclMapper.class);
private MybatisShiroDeptMapper deptMapper = SpringContextHolder.getBean(MybatisShiroDeptMapper.class);
private MybatisShiroUserMapper userMapper = SpringContextHolder.getBean(MybatisShiroUserMapper.class);
private MybatisShiroRoleMapper roleMapper = SpringContextHolder.getBean(MybatisShiroRoleMapper.class);
private MybatisShiroRoleUserMapper roleUserMapper = SpringContextHolder.getBean(MybatisShiroRoleUserMapper.class);


public static Constant me() {
return SpringContextHolder.getBean("constantFactory");
}

@Override
public String getRealNameById(Integer userId) {
MybatisShiroUser user = userMapper.selectByPrimaryKey(userId);
if (user != null) {
return user.getRealname();
}
return null;
}

@Override
public String getUsernameById(Integer userId) {
MybatisShiroUser user = userMapper.selectByPrimaryKey(userId);
if (user != null && StrUtil.isNotEmpty(user.getUsername())) {
return user.getUsername();
}
return null;
}

@Override
public String getRoleName(Integer roleId) {
MybatisShiroRole role = roleMapper.selectByPrimaryKey(roleId);
if (role != null && StrUtil.isNotEmpty(role.getName())) {
return role.getName();
}
return null;
}

@Override
public List<String> getRoleNames(List<Integer> roleIds) {
return roleIds.stream().map(id -> getRoleName(id)).collect(Collectors.toList());
}

@Override
public List<Integer> getRoleIds(Integer userId) {
MybatisShiroRoleUser param = new MybatisShiroRoleUser();
param.setUserId(userId);
List<MybatisShiroRoleUser> roleUser = roleUserMapper.select(param);
return roleUser.stream().map(v -> v.getRoleId()).collect(Collectors.toList());
}

@Override
public String getDeptName(Integer deptId) {
MybatisShiroDept dept = deptMapper.selectByPrimaryKey(deptId);
if (dept != null && StrUtil.isNotEmpty(dept.getName())) {
return dept.getName();
}
return null;
}

@Override
public String getAclName(Integer aclId) {
MybatisShiroAcl acl = aclMapper.selectByPrimaryKey(aclId);
if (acl != null && StrUtil.isNotEmpty(acl.getName())) {
return acl.getName();
}
return null;
}

@Override
public String getAclNameByCode(String code) {
if (StrUtil.isNotBlank(code)) {
return null;
} else {
MybatisShiroAcl param = new MybatisShiroAcl();
param.setCode(code);
MybatisShiroAcl acl = aclMapper.selectOne(param);
if (acl != null && StrUtil.isNotEmpty(acl.getName())) {
return acl.getName();
}
return null;
}
}

@Override
public String getUserStatusName(Integer code) {
return UserStatusEnum.valueOf(code);
}

@Override
public String getAclStatusName(Integer code) {
return AclStatusEnum.valueOf(code);
}

@Override
public List<Integer> getSubDeptId(Integer deptId) {
Example example = new Example(MybatisShiroDept.class);
example.createCriteria().andLike("level", "%" + deptId + "%");

List<MybatisShiroDept> deptList = deptMapper.selectByExample(example);

ArrayList<Integer> deptIds = Lists.newArrayList();

if (deptList != null || deptList.size() > 0) {
for (MybatisShiroDept dept : deptList) {
deptIds.add(dept.getId());
}
}

return deptIds;
}

@Override
public List<Integer> getParentDeptIds(Integer deptId) {
MybatisShiroDept dept = deptMapper.selectByPrimaryKey(deptId);
String level = dept.getLevel();
ArrayList<Integer> parentIds = Lists.newArrayList();
for (String parentId : level.split(COMMA)) {
parentIds.add(Integer.valueOf(parentId));
}
return parentIds;
}
}

+ 24
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/domain/LoginParam.java View File

@@ -0,0 +1,24 @@
package com.xkcoding.springbootdemorabcshiromybatis.domain;

import lombok.Data;

/**
* <p>
* 登录参数
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.domain
* @description: 登录参数
* @author: yangkai.shen
* @date: Created in 2017/12/7 下午3:45
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Data
public class LoginParam {
private String username;
private String password;
private String kaptcha;
private Boolean rememberMe;
}

+ 0
- 48
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/enums/AclStatusEnum.java View File

@@ -1,48 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.enums;

import lombok.Getter;

/**
* <p>
* 权限状态的枚举类
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.enums
* @description: 权限状态的枚举类
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午3:38
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Getter
public enum AclStatusEnum {
DISABLE(0, "禁用"), ENABLE(1, "启用");

private Integer code;
private String message;

AclStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}

/**
* 根据状态码返回状态
*
* @param code 状态码
* @return 状态
*/
public static String valueOf(Integer code) {
if (code == null) {
return null;
} else {
for (AclStatusEnum statusEnum : AclStatusEnum.values()) {
if (statusEnum.getCode().equals(code)) {
return statusEnum.getMessage();
}
}
return null;
}
}
}

+ 0
- 48
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/enums/UserStatusEnum.java View File

@@ -1,48 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.enums;

import lombok.Getter;

/**
* <p>
* 用户状态的枚举类
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.enums
* @description: 用户状态的枚举类
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午3:38
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Getter
public enum UserStatusEnum {
DELETED(-1, "已删除"), DISABLE(0, "禁用"), ENABLE(1, "启用");

private Integer code;
private String message;

UserStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}

/**
* 根据状态码返回状态
*
* @param code 状态码
* @return 状态
*/
public static String valueOf(Integer code) {
if (code == null) {
return null;
} else {
for (UserStatusEnum statusEnum : UserStatusEnum.values()) {
if (statusEnum.getCode().equals(code)) {
return statusEnum.getMessage();
}
}
return null;
}
}
}

+ 0
- 96
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/MyShiroRealm.java View File

@@ -1,96 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.shiro;

import com.google.common.collect.Sets;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xkcoding.springbootdemorabcshiromybatis.dao.MybatisShiroUserMapper;
import com.xkcoding.springbootdemorabcshiromybatis.model.MybatisShiroUser;
import com.xkcoding.springbootdemorabcshiromybatis.shiro.factory.Shiro;
import com.xkcoding.springbootdemorabcshiromybatis.shiro.factory.ShiroFactroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Set;

/**
* shiro 身份校验
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.shiro
* @description: shiro 身份校验
* @author: yangkai.shen
* @date: Created in 2017/11/29 下午3:39
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private MybatisShiroUserMapper mybatisShiroUserMapper;

/**
* 身份认证: Authentication 用来验证用户信息
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("【身份认证】:进入doGetAuthenticationInfo()");

Shiro shiroFactory = ShiroFactroy.me();
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
MybatisShiroUser user = shiroFactory.user(token.getUsername());
ShiroUser shiroUser = shiroFactory.shiroUser(user);
SimpleAuthenticationInfo info = shiroFactory.info(shiroUser, user, super.getName());
return info;

}

/**
* 授权验证
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("【授权验证】:进入doGetAuthorizationInfo()");
Shiro shiroFactory = ShiroFactroy.me();
ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal();
List<Integer> roleList = shiroUser.getRoleList();

Set<String> aclSet = Sets.newHashSet();
Set<String> roleNameSet = Sets.newHashSet(shiroUser.getRoleNames());

for (Integer roleId : roleList) {
List<String> acls = shiroFactory.findAclsByRoleId(roleId);
for (String acl : acls) {
if (StrUtil.isNotEmpty(acl)) {
aclSet.add(acl);
}
}
}

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(aclSet);
info.addRoles(roleNameSet);
return info;
}

@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
md5CredentialsMatcher.setHashAlgorithmName(ShiroUtil.HASH_ALGORITHM_NAME);
md5CredentialsMatcher.setHashIterations(ShiroUtil.HASH_ITERATIONS);
super.setCredentialsMatcher(md5CredentialsMatcher);
}
}

+ 0
- 34
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/ShiroUser.java View File

@@ -1,34 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.shiro;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
* <p>
* 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.shiro
* @description: 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午3:26
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Data
public class ShiroUser implements Serializable {

private static final long serialVersionUID = 1L;

private Integer id;
private String username;
private String realname;
private Integer deptId;
private String deptName;
private List<Integer> roleList;
private List<String> roleNames;

}

+ 0
- 270
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/ShiroUtil.java View File

@@ -1,270 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.shiro;

import com.xkcoding.springbootdemorabcshiromybatis.constrant.factory.Constant;
import com.xkcoding.springbootdemorabcshiromybatis.constrant.factory.ConstantFactory;
import com.xkcoding.util.T;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;

import java.util.List;

/**
* <p>
* shiro 工具类
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.shiro
* @description: shiro 工具类
* @author: yangkai.shen
* @date: Created in 2017/12/1 下午6:02
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
public class ShiroUtil {

/**
* 加盐参数
*/
public final static String HASH_ALGORITHM_NAME = "MD5";

/**
* 循环次数
*/
public final static int HASH_ITERATIONS = 1;

/**
* shiro密码加密工具类
*
* @param credentials 密码
* @param saltSource 密码盐
* @return 加密后的字符串
*/
public static String md5(String credentials, String saltSource) {
ByteSource salt = new Md5Hash(saltSource);
return new SimpleHash(HASH_ALGORITHM_NAME, credentials, salt, HASH_ITERATIONS).toString();
}

/**
* 获取随机盐值
*
* @return 获取随机盐值
*/
public static String getRandomSalt() {
return T.UUID();
}

/**
* 获取当前 Subject
*
* @return Subject
*/
public static Subject getSubject() {
return SecurityUtils.getSubject();
}

/**
* 获取封装的 ShiroUser
*
* @return ShiroUser
*/
public static ShiroUser getUser() {
if (isGuest()) {
return null;
} else {
return (ShiroUser) getSubject().getPrincipals().getPrimaryPrincipal();
}
}

/**
* 从shiro获取session
*/
public static Session getSession() {
return getSubject().getSession();
}

/**
* 获取shiro指定的sessionKey
*/
@SuppressWarnings("unchecked")
public static <T> T getSessionAttr(String key) {
Session session = getSession();
return session != null ? (T) session.getAttribute(key) : null;
}

/**
* 设置shiro指定的sessionKey
*/
public static void setSessionAttr(String key, Object value) {
Session session = getSession();
session.setAttribute(key, value);
}

/**
* 移除shiro指定的sessionKey
*/
public static void removeSessionAttr(String key) {
Session session = getSession();
if (session != null) {
session.removeAttribute(key);
}
}

/**
* 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用
*
* @param roleName 角色名
* @return 属于该角色:true,否则false
*/
public static boolean hasRole(String roleName) {
return getSubject() != null && roleName != null && roleName.length() > 0 && getSubject().hasRole(roleName);
}

/**
* 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
*
* @param roleName 角色名
* @return 不属于该角色:true,否则false
*/
public static boolean lacksRole(String roleName) {
return !hasRole(roleName);
}

/**
* 验证当前用户是否属于以下任意一个角色。
*
* @param roleNames 角色列表
* @return 属于:true,否则false
*/
public static boolean hasAnyRoles(String roleNames) {
boolean hasAnyRole = false;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(Constant.COMMA)) {
if (subject.hasRole(role.trim())) {
hasAnyRole = true;
break;
}
}
}
return hasAnyRole;
}

/**
* 验证当前用户是否属于以下所有角色。
*
* @param roleNames 角色列表
* @return 属于:true,否则false
*/
public static boolean hasAllRoles(String roleNames) {
boolean hasAllRole = true;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(Constant.COMMA)) {
if (!subject.hasRole(role.trim())) {
hasAllRole = false;
break;
}
}
}
return hasAllRole;
}

/**
* 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
public static boolean hasPermission(String permission) {
return getSubject() != null && permission != null && permission.length() > 0 && getSubject().isPermitted(permission);
}

/**
* 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
public static boolean lacksPermission(String permission) {
return !hasPermission(permission);
}

/**
* 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用
*
* @return 通过身份验证:true,否则false
*/
public static boolean isAuthenticated() {
return getSubject() != null && getSubject().isAuthenticated();
}

/**
* 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。
*
* @return 没有通过身份验证:true,否则false
*/
public static boolean notAuthenticated() {
return !isAuthenticated();
}

/**
* 认证通过或已记住的用户。与guset搭配使用。
*
* @return 用户:true,否则 false
*/
public static boolean isUser() {
return getSubject() != null && getSubject().getPrincipal() != null;
}

/**
* 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用
*
* @return 访客:true,否则false
*/
public static boolean isGuest() {
return !isUser();
}

/**
* 输出当前用户信息,通常为登录帐号信息。
*
* @return 当前用户信息
*/
public static String principal() {
if (getSubject() != null) {
Object principal = getSubject().getPrincipal();
return principal.toString();
}
return "";
}

/**
* 获取当前用户的部门数据范围的集合
*/
public static List<Integer> getDeptDataScope() {
Integer deptId = getUser().getDeptId();
List<Integer> subDeptIds = ConstantFactory.me().getSubDeptId(deptId);
subDeptIds.add(deptId);
return subDeptIds;
}

/**
* 判断当前用户是否是超级管理员
*/
public static boolean isAdmin() {
List<String> roleNameList = getUser().getRoleNames();
for (String roleName : roleNameList) {
if (roleName.equals(Constant.ADMIN_NAME)) {
return true;
}
}
return false;
}

}


+ 0
- 66
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/factory/Shiro.java View File

@@ -1,66 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.shiro.factory;

import com.xkcoding.springbootdemorabcshiromybatis.model.MybatisShiroUser;
import com.xkcoding.springbootdemorabcshiromybatis.shiro.ShiroUser;
import org.apache.shiro.authc.SimpleAuthenticationInfo;

import java.util.List;

/**
* <p>
* 定义 shiro realm 所需数据的接口
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.shiro.factory
* @description: 定义 shiro realm 所需数据的接口
* @author: yangkai.shen
* @date: Created in 2017/12/6 下午3:24
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
public interface Shiro {

/**
* 根据用户名数据库存储的用户信息
*
* @param username 用户名
* @return 数据库存储的用户信息
*/
MybatisShiroUser user(String username);

/**
* 根据系统用户获取 shiro 的用户
*
* @param user 数据库保存的用户
* @return 自定义的用户对象
*/
ShiroUser shiroUser(MybatisShiroUser user);

/**
* 根据角色id获取权限列表
*
* @param roleId 角色id
* @return 权限列表
*/
List<String> findAclsByRoleId(Integer roleId);

/**
* 根据角色id获取角色名称
*
* @param roleId 角色id
* @return 角色名称
*/
String findRoleNameByRoleId(Integer roleId);

/**
* 获取 shiro 的认证信息
*
* @param shiroUser 自定义返回的 user 对象
* @param user 数据库保存的 user 对象
* @param realmName 真实姓名
* @return shiro的认证信息
*/
SimpleAuthenticationInfo info(ShiroUser shiroUser, MybatisShiroUser user, String realmName);

}

+ 0
- 95
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/shiro/factory/ShiroFactroy.java View File

@@ -1,95 +0,0 @@
package com.xkcoding.springbootdemorabcshiromybatis.shiro.factory;

import com.xkcoding.springbootdemorabcshiromybatis.constrant.factory.ConstantFactory;
import com.xkcoding.springbootdemorabcshiromybatis.dao.MybatisShiroAclMapper;
import com.xkcoding.springbootdemorabcshiromybatis.dao.MybatisShiroUserMapper;
import com.xkcoding.springbootdemorabcshiromybatis.enums.UserStatusEnum;
import com.xkcoding.springbootdemorabcshiromybatis.model.MybatisShiroUser;
import com.xkcoding.springbootdemorabcshiromybatis.shiro.ShiroUser;
import com.xkcoding.springbootdemorabcshiromybatis.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@DependsOn("springContextHolder")
@Slf4j
public class ShiroFactroy implements Shiro {

@Autowired
private MybatisShiroUserMapper userMapper;

@Autowired
private MybatisShiroAclMapper aclMapper;

public static Shiro me() {
return SpringContextHolder.getBean(Shiro.class);
}

@Override
public MybatisShiroUser user(String username) {

MybatisShiroUser user = userMapper.findByUsername(username);

// 账号不存在
if (null == user) {
log.error("【登录失败】:账号不存在");
throw new CredentialsException("账号不存在");
}
// 账号被冻结
if (user.getStatus().equals(UserStatusEnum.DISABLE.getCode())) {
log.error("【登录失败】:用户已被冻结");
throw new DisabledAccountException("登录失败,用户已被冻结");
} else if (user.getStatus().equals(UserStatusEnum.DELETED.getCode())) {
log.error("【登录失败】:没有该用户");
throw new DisabledAccountException("登录失败,没有该用户");
}
return user;
}

@Override
public ShiroUser shiroUser(MybatisShiroUser user) {
ShiroUser shiroUser = new ShiroUser();

shiroUser.setId(user.getId());
shiroUser.setUsername(user.getUsername());
shiroUser.setDeptId(user.getDeptId());
shiroUser.setDeptName(ConstantFactory.me().getDeptName(user.getDeptId()));
shiroUser.setRealname(user.getRealname());

List<Integer> roleList = ConstantFactory.me().getRoleIds(user.getId());
List<String> roleNameList = ConstantFactory.me().getRoleNames(roleList);
shiroUser.setRoleList(roleList);
shiroUser.setRoleNames(roleNameList);

return shiroUser;
}

@Override
public List<String> findAclsByRoleId(Integer roleId) {
return aclMapper.getResUrlsByRoleId(roleId);
}

@Override
public String findRoleNameByRoleId(Integer roleId) {
return ConstantFactory.me().getRoleName(roleId);
}

@Override
public SimpleAuthenticationInfo info(ShiroUser shiroUser, MybatisShiroUser user, String realmName) {
String credentials = user.getPassword();
// 密码加盐处理
String source = user.getSalt();
ByteSource credentialsSalt = new Md5Hash(source);
return new SimpleAuthenticationInfo(shiroUser, credentials, credentialsSalt, realmName);
}

}

+ 147
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/util/IpUtil.java View File

@@ -0,0 +1,147 @@
package com.xkcoding.springbootdemorabcshiromybatis.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* <p>
* 获取 IP 工具类
* </p>
*
* @package: com.xkcoding.springbootdemorabcshiromybatis.util
* @description: 获取 IP 工具类
* @author: yangkai.shen
* @date: Created in 2017/12/7 上午11:13
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
public class IpUtil {
public final static String ERROR_IP = "127.0.0.1";

public final static Pattern pattern = Pattern.
compile("(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})");

/**
* 取外网IP
*
* @param request
* @return
*/
public static String getRemoteIp(HttpServletRequest request) {
String ip = request.getHeader("x-real-ip");
if (ip == null) {
ip = request.getRemoteAddr();
}

//过滤反向代理的ip
String[] stemps = ip.split(",");
if (stemps != null && stemps.length >= 1) {
//得到第一个IP,即客户端真实IP
ip = stemps[0];
}

ip = ip.trim();
if (ip.length() > 23) {
ip = ip.substring(0, 23);
}

return ip;
}

/**
* 获取用户的真实ip
*
* @param request
* @return
*/
public static String getUserIP(HttpServletRequest request) {

// 优先取X-Real-IP
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}

if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = ERROR_IP;
}
}

if ("unknown".equalsIgnoreCase(ip)) {
ip = ERROR_IP;
return ip;
}

int pos = ip.indexOf(',');
if (pos >= 0) {
ip = ip.substring(0, pos);
}

return ip;
}

public static String getLastIpSegment(HttpServletRequest request) {
String ip = getUserIP(request);
if (ip != null) {
ip = ip.substring(ip.lastIndexOf('.') + 1);
} else {
ip = "0";
}
return ip;
}

public static boolean isValidIP(HttpServletRequest request) {
String ip = getUserIP(request);
return isValidIP(ip);
}

/**
* 判断我们获取的ip是否是一个符合规则ip
*
* @param ip
* @return
*/
public static boolean isValidIP(String ip) {
if (StringUtils.isEmpty(ip)) {
log.debug("ip is null. valid result is false");
return false;
}

Matcher matcher = pattern.matcher(ip);
boolean isValid = matcher.matches();
log.debug("valid ip:" + ip + " result is: " + isValid);
return isValid;
}

public static String getLastServerIpSegment() {
String ip = getServerIP();
if (ip != null) {
ip = ip.substring(ip.lastIndexOf('.') + 1);
} else {
ip = "0";
}
return ip;
}

public static String getServerIP() {
InetAddress inet;
try {
inet = InetAddress.getLocalHost();
String hostAddress = inet.getHostAddress();
return hostAddress;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "127.0.0.1";
}
}

+ 62
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/java/com/xkcoding/springbootdemorabcshiromybatis/util/JsonMapper.java View File

@@ -0,0 +1,62 @@
package com.xkcoding.springbootdemorabcshiromybatis.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
* Json 转化工具类
*
* @package: com.xkcoding.springbootdemoaoplog.util
* @description:Json 转化工具类
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:36
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
public class JsonMapper {
private static ObjectMapper objectMapper = new ObjectMapper();

/**
* 对象转 json 字符串
*
* @param src 元对象
* @param <T> 类型
* @return json 字符串
*/
public static <T> String obj2Str(T src) {
if (src == null) {
return null;
}
try {
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
} catch (IOException e) {
log.error("【JSON 转换:对象 --> 字符串】,异常堆栈:{}", e);
return null;
}
}

/**
* json 字符串转化为对象
*
* @param src 源 json 字符串
* @param typeReference 转化后的类型
* @param <T> 类型
* @return 返回转化后的对象
*/
public static <T> T str2Obj(String src, TypeReference<T> typeReference) {
if (src == null || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class) ? src : objectMapper.readValue(src, typeReference));
} catch (Exception e) {
log.error("【JSON 转换:字符串 --> 对象】,异常堆栈:{}", e);
return null;
}
}
}

+ 5
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/resources/application.yml View File

@@ -2,6 +2,11 @@ server:
port: 8080
context-path: /demo
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
content-type: text/html
cache: false
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss


+ 2
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/resources/init-sql/shiro.sql View File

@@ -20,6 +20,8 @@ INSERT INTO `mybatis_shiro_dept` VALUES (3, '运营部', 1, '0,1', 1, '运营部
INSERT INTO `mybatis_shiro_dept` VALUES (4, '战略部', 1, '0,1', 2, '战略部', '系统', '2017-12-01 00:00:00', '127.0.0.1');
INSERT INTO `mybatis_shiro_dept` VALUES (5, '软件部', 2, '0,1,2', 0, '软件部', '系统', '2017-12-01 00:00:00', '127.0.0.1');
INSERT INTO `mybatis_shiro_dept` VALUES (6, '硬件部', 2, '0,1,2', 1, '硬件部', '系统', '2017-12-01 00:00:00', '127.0.0.1');
INSERT INTO `mybatis_shiro_dept` VALUES (7, '采购部', 6, '0,1,2,6', 0, '采购部', '系统', '2017-12-01 00:00:00', '127.0.0.1');
INSERT INTO `mybatis_shiro_dept` VALUES (8, '维修部', 6, '0,1,2,6', 1, '维修部', '系统', '2017-12-01 00:00:00', '127.0.0.1');

-- 用户表 --
DROP TABLE IF EXISTS `mybatis_shiro_user`;


+ 31
- 0
spring-boot-demo-rabc-shiro-mybatis/src/main/resources/templates/index.html View File

@@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Document</title>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
</head>
<body>
用户名:<input type="text" id="ipt_username" name="username"/>
密码:<input type="password" id="ipt_password" name="password"/>
<button id="btn_login"> 登录</button>

<script>
$(function () {
$("#btn_login").click(function () {
var username = $("#ipt_username").val();
var password = $("#ipt_password").val();
axios.post('/demo/ajaxLogin', {username, password}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
})
});
});
</script>
</body>
</html>

Loading…
Cancel
Save