SpringBoot 整合shiro框架

网上有很多整合shiro的博客分享,但是貌似没找到一个完整,并且能够实现的。不是包的问题,就是代码的问题,也可能是自己的问题,或者版本的问题。所以,整理了一版自己已应用的.

maven依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.2.RELEASE</version>
    <relativePath/>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>
        <!--整合模板打开注释-->
        <!--<dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!--<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.11</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
</dependencies>

MyShiroRealm.java(权限配置)

import com.simple.users.dto.entity.UserInfo;
import com.simple.users.service.UserInfoService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

public class MyShiroRealm extends AuthorizingRealm {

	@Resource
	private UserInfoService userInfoService;

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
		/*
		 * for (SysRole role : userInfo.getRoleList()) {
		 * authorizationInfo.addRole(role.getRole()); for (SysPermission p :
		 * role.getPermissions()) {
		 * authorizationInfo.addStringPermission(p.getPermission()); } }
		 */
		// 获取用户角色
		Set<String> roleSet = new HashSet<String>();
		roleSet.add("100002");
		info.setRoles(roleSet);

		// 获取用户权限
		Set<String> permissionSet = new HashSet<String>();
		permissionSet.add("权限添加");
		permissionSet.add("权限删除");
		info.setStringPermissions(permissionSet);

		return info;
	}

	/**
	 * 主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。
	 * 
	 * @param authcToken
	 * @return
	 * @throws AuthenticationException
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
			throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String username = token.getUsername();
		UserInfo userInfo = userInfoService.findByUsername(username);
		if (userInfo == null) {
			return null;
		}
		String password = new String((char[]) token.getCredentials());
		String pwdMd5 = (new Md5Hash(password, username)).toHex();
		System.out.println("----->>pwdMd5====" + pwdMd5);

		UserInfo user = new UserInfo();
		user.setUsername(username);
		user.setPassword(pwdMd5);

		return new SimpleAuthenticationInfo(user, pwdMd5, ByteSource.Util.bytes(username), getName());
	}

}

ShiroConfig.java(shiro配置)

import com.simple.utils.Base64Util;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {
	/**
	 * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
	 * 开启thymeleaf模板访问,注释则使用配置的jsp或者HTML访问模式
	 * @return
	 */
//	@Bean
//	public ShiroDialect shiroDialect() {
//		return new ShiroDialect();
//	}

	@Bean
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
		System.out.println("--------ShiroConfiguration.shirFilter()");
		// 添加安全管理器
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 如果不设置默认会自动寻找Web工程根目录下的"/index.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/");
		// 登录成功后要跳转的链接
		// shiroFilterFactoryBean.setSuccessUrl("/?path=user/main");
		// 添加shiro内置过滤器
		/**
		 * anon:表示可以匿名使用。 authc:表示需要认证(登录)才能使用,没有参数
		 * 
		 * roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,
		 * 例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
		 *
		 * perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,
		 * 例如/admins/user/**=perms["user:add:*,user:modify:*"],
		 * 当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
		 *
		 * rest:根据请求的方法,相当于/admins/user/**=perms[user:method]
		 * ,其中method为post,get,delete等。
		 *
		 * port:当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,
		 * 其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,
		 * queryString是你访问的url里的?后面的参数。 authcBasic:没有参数表示httpBasic认证
		 *
		 * ssl:表示安全的url请求,协议为https user:当登入操作时不做检查
		 */
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 静态资源的处理
		filterChainDefinitionMap.put("/js/**", "anon");
		filterChainDefinitionMap.put("/css/**", "anon");
		filterChainDefinitionMap.put("/img/**", "anon");
		filterChainDefinitionMap.put("/fonts/**", "anon");
		filterChainDefinitionMap.put("/plugins/**", "anon");
//		// 请求路径的处理
		filterChainDefinitionMap.put("/H-PLE/**", "authc");

		// 退出系统的过滤器
		filterChainDefinitionMap.put("/userInfo/logout", "logout");
		filterChainDefinitionMap.put("/userInfo/login", "anon");

		// 默认所有资源必须认证才能访问
		filterChainDefinitionMap.put("/**", "authc");

		// 未授权界面;
		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(1);// 散列的次数(默认一次),比如散列两次,相当于 md5(md5(""));
		return hashedCredentialsMatcher;
	}

	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm());
		// 注入记住我管理器;
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

	/**
	 * cookie对象;
	 * 
	 * @return
	 */
	public SimpleCookie rememberMeCookie() {
		// 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		// <!-- 记住我cookie生效时间30天 ,单位秒;-->
		simpleCookie.setMaxAge(2592000);
		return simpleCookie;
	}

	/**
	 * cookie管理对象;记住我功能
	 * 
	 * @return
	 */
	public CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		// rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
		try {
			cookieRememberMeManager.setCipherKey(Base64Util.decode("3AvVhmFLUs0KTA3Kprsdag=="));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cookieRememberMeManager;
	}

	/**
	 * 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	@Bean(name = "simpleMappingExceptionResolver")
	public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
		SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
		Properties mappings = new Properties();
		mappings.setProperty("DatabaseException", "databaseError");// 数据库异常处理
		mappings.setProperty("UnauthorizedException", "403");
		r.setExceptionMappings(mappings); // None by default
		r.setDefaultErrorView("error"); // No default
		r.setExceptionAttribute("ex"); // Default is "exception"
		// r.setWarnLogCategory("example.MvcLogger"); // No default
		return r;
	}
}

UserController.java(登录应用)

@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST, produces = { "application/json; charset=UTF-8" })
public Object login(@RequestBody JSONObject jsonP) {
	JSONObject result = new JSONObject();
	JSONObject resultHd = new JSONObject();
	JSONObject resultBd = new JSONObject();
	String userName = Null.nullToSpace(jsonP.getString("userName"));
	String passWord = Null.nullToSpace(jsonP.getString("passWord"));
	Boolean rememberMe = jsonP.getBoolean("agree");
	System.out.println("userName--" + userName + "; passWord--" + passWord + "; rememberMe--" + rememberMe);
	UsernamePasswordToken token = new UsernamePasswordToken(userName, passWord, rememberMe);
	try {
		SecurityUtils.getSubject().login(token);
		resultHd.put("error", "成功");
		resultHd.put("errorCode", "0");
		resultHd.put("data", new HashMap());
	} catch (UnknownAccountException e) {
		resultHd.put("error", "失败");
		resultHd.put("errorCode", "2");
		resultHd.put("data", new HashMap());
		e.printStackTrace();
	} catch (IncorrectCredentialsException e) {
		resultHd.put("error", "失败");
		resultHd.put("errorCode", "3");
		resultHd.put("data", new HashMap());
		e.printStackTrace();
	} catch (AuthenticationException e) {
		// 其他错误,比如锁定,如果想单独处理请单独catch处理
		resultHd.put("error", "失败");
		resultHd.put("errorCode", "1");
		resultHd.put("data", new HashMap());
		e.printStackTrace();
	}

	result.put("header", resultHd);
	result.put("body", resultBd);

	return result;
}

User实体:

权限类:SysPermission.java

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Getter
@Setter
@ToString
@Entity
public class SysPermission implements Serializable {
	@Id
	@GeneratedValue
	private Integer id;// 主键.
	private String name;// 名称.
	@Column(columnDefinition = "enum('menu','button')")
	private String resourceType;// 资源类型,[menu|button]
	private String url;// 资源路径.
	private String permission; // 权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
	private Long parentId; // 父编号
	private String parentIds; // 父编号列表
	private Boolean available = Boolean.FALSE;
	@ManyToMany
	@JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "permissionId") }, inverseJoinColumns = {
			@JoinColumn(name = "roleId") })
	private List<SysRole> roles;
}
----------------------------------------------------------------------------------------
角色类:SysRole.java

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;
import java.util.List;

@Getter
@Setter
@ToString
@Entity
public class SysRole {
	@Id
	@GeneratedValue
	private Integer id; // 编号
	private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的:
	private String description; // 角色描述,UI界面显示使用
	private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户

	// 角色 -- 权限关系:多对多关系;
	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = {
			@JoinColumn(name = "permissionId") })
	private List<SysPermission> permissions;

	// 用户 - 角色关系定义;
	@ManyToMany
	@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = {
			@JoinColumn(name = "uid") })
	private List<UserInfo> userInfos;// 一个角色对应多个用户

	// 省略 get set 方法
}
----------------------------------------------------------------------------------------
用户类:UserInfo.java

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Getter
@Setter
@ToString
@Entity
public class UserInfo implements Serializable {
	@Id
	@GeneratedValue
	private Integer uid;
	@Column(unique = true)
	private String username;// 帐号
	private String name;// 名称(昵称或者真实姓名,不同系统不同定义)
	private String password; // 密码;
	private String salt;// 加密密码的盐
	private byte state;// 用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定.
	@ManyToMany(fetch = FetchType.EAGER) // 立即从数据库中进行加载数据;
	@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = {
			@JoinColumn(name = "roleId") })
	private List<SysRole> roleList;// 一个用户具有多个角色

	/**
	 * 密码盐.
	 * 
	 * @return
	 */
	public String getCredentialsSalt() {
		return this.username + this.salt;
	}
	// 重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解
}

数据:

用户表:账号admin	密码admin
INSERT INTO `user_info`(`uid`, `name`, `password`, `salt`, `state`, `username`) VALUES (1, '管理员', 'f6fdffe48c908deb0f4c3bd36c032e72', '87', 0, 'admin');

权限表:
INSERT INTO `sys_permission`(`id`, `available`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (1, b'0', '用户管理', 0, '0/', 'userInfo:view', 'menu', 'userInfo/userList');
INSERT INTO `sys_permission`(`id`, `available`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (2, b'0', '用户添加', 1, '0/1', 'userInfo:add', 'button', 'userInfo/userAdd');
INSERT INTO `sys_permission`(`id`, `available`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (3, b'0', '用户删除', 1, '0/1', 'userInfo:del', 'button', 'userInfo/userDel');

角色表:
INSERT INTO `sys_role`(`id`, `available`, `description`, `role`) VALUES (1, b'0', '管理员', 'admin');
INSERT INTO `sys_role`(`id`, `available`, `description`, `role`) VALUES (2, b'0', 'VIP会员', 'vip');

里面的Null类引用,可以在 https://blog.csdn.net/weixin_42614447/article/details/86536425 (Java 一些常用工具类)这里面找工具类。

目录结构

yml配置:

druid:
    stat:
        mergeSql: true;
        slowSqlMillis: 5000
logging:
    level:
        com: DEBUG
mybatis:
    mapper-locations: classpath:com/simple/*/dto/maps/*Mapper.xml
    type-aliases-package: com.simple.*
server:
    port: 8081
    servlet:
        context-path: /
spring:
    datasource:
        driverClassName: com.mysql.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3306/oa?characterEncoding=utf8&autoReconnect=true&useSSL=false&useAffectedRows=true
        username: root
        password: root
        filters: stat,wall,log4j
        initialSize: 5
        maxActive: 20
        maxPoolPreparedStatementPerConnectionSize: 20
        maxWait: 60000
        minEvictableIdleTimeMillis: 300000
        minIdle: 5
        poolPreparedStatements: true
        testOnBorrow: false
        testOnReturn: false
        testWhileIdle: true
        timeBetweenEvictionRunsMillis: 60000
        validationQuery: SELECT 1 FROM DUAL
    http:
        encoding:
            charset: UTF-8
            enabled: true
    jpa:
        show-sql: true
        hibernate:
            ddl-auto: update
            naming:
                physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        properties:
            hibernate:
                dialect: org.hibernate.dialect.MySQL5Dialect
    mvc:
        locale: zh_CN
        view:
            prefix: /WEB-INF/jsp/
            suffix: .jsp
        static-path-pattern: /**
    #资源名称md5方式
    resources:
        chain:
            strategy:
                content:
                    enabled: true
                    paths: /**

以上就是shiro完整代码,如有疑问,随时评论.

转载请注明出处!

猜你喜欢

转载自blog.csdn.net/weixin_42614447/article/details/86537265