springBoot shiro整合

Shiro是一个权限框架,他提供了很方便的权限认证和登录的功能。而springboot必然提供了和shiro整合的功能,接下来就用springboot结合mybatis整合shiro。

Step1. 准备数据库表
 这里主要涉及五张表:用户表(user),角色表(role),权限表(permission),用户-角色表(user_role),角色-权限表 (role_permission)。
   注意:用户和角色是多对多的关系,角色和权限是多对多的关系。

   用户表的字段有:id(主键),username(用户名),password(密码),salt(在密码加密的时候会用到)。

   角色表的字段有:id(主键),rolename(角色名称)。
   权限表的字段有:id(主键),permissionname(权限名称)。
   用户-角色表的字段有:id(主键),userId(用户id), roleId(角色id)。
   角色-权限表的字段有:id(主键),roleId(角色id), permissionId(权限id)。

Step2. 新建一个springBoot项目,导入以下的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>shiroProjectDemo</artifactId>
	<version>1.0.0-SNAPSHOT</version>	
	<packaging>jar</packaging>

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

	<properties>
		<java.version>1.8</java.version>
		<shiro.version>1.3.2</shiro.version>
		<commons-lang3>3.7</commons-lang3>
		<druid.version>1.1.6</druid.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>${commons-lang3}</version>
		</dependency>
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.1</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>${druid.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

Step3. 对数据库连接池进行配置。

我采用的是阿里巴巴的Druid数据源,在application.yml中对他进行相关配置。
#默认使用配置
spring:
  profiles:
      active: dev
#公共配置与profiles选择无关
mybatis:
  typeAliasesPackage: com.example.demo.dao
  mapperLocations: classpath:mapper/*.xml

---

#开发配置
spring:
  profiles: dev

  datasource:
    url: jdbc:mysql://localhost:3306/shirodatabase?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

  session:
    store-type: none

注:还应该在数据库连接池的配置类完成对数据库连接池的配置(这里不做过多的描述)。

Step4. 建立和表相对应的实体类

public class User {
  private Integer id;
  private String username;
  private String password;
  private String salt;
  //用户的角色id
  private List<String> roleIds;
}

public class Role {
    private Integer id;
    private String roleName;
}

public class Permission {
    private Integer id;
    private String permissionName;

   }

public class UserRole {
    private Integer id;
    private String userId;
    private String roleId;
}

public class RolePermission {
    private Integer id;
    private String roleName;
    private String permissionName;
}

接着就可以书写mapper了,mapper所在的位置要和Application类中所配置的包扫描的位置保持一致。

userMapper.xml如下:
<?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.example.demo.dao.UserMapper">
  <resultMap id="BaseResultMap" type="com.example.demo.dao.entity.User">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="username" jdbcType="VARCHAR" property="username" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="salt" jdbcType="VARCHAR" property="salt" />
  </resultMap>
  <sql id="Base_Column_List">
    id, username, password , salt
  </sql>

  <insert id="insertSelective" parameterType="com.example.demo.dao.entity.User">
    insert into user
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="username != null">
        username,
      </if>
      <if test="password != null">
        password,
      </if>
      <if test="salt != null">
        salt,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="username != null">
        #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        #{password,jdbcType=VARCHAR},
      </if>
      <if test="salt != null">
        #{salt,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <select id="getUser" resultMap="BaseResultMap" resultType="java.lang.String">
    SELECT
    <include refid="Base_Column_List" />
    FROM user
    WHERE username = #{username,jdbcType = VARCHAR}
  </select>
</mapper>
roleMapper.xml如下:
<?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.example.demo.dao.RoleMapper">
  <resultMap id="BaseResultMap" type="com.example.demo.dao.entity.Role">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="roleId" jdbcType="VARCHAR" property="roleId" />
  </resultMap>

  <select id="listByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  SELECT  role.*  FROM  role,user_role WHERE role.id = user_role.roleId AND  userId = #{userId,jdbcType = INTEGER}
  </select>
</mapper>
permissionMapper.xml如下:
<?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.example.demo.dao.PermissionMapper">
  <resultMap id="BaseResultMap" type="com.example.demo.dao.entity.Permission">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="permissionname" jdbcType="VARCHAR" property="permissionName" />
  </resultMap>
  <sql id="Base_Column_List">
    id,permissionname
  </sql>
  <select id="listPermissionByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select distinct p.* from permission p inner join role_permission rp on p.id = rp.permissionId inner join role_user ru on ru.id = rp.roleId where ru.userId = #{userId}
  </select>
</mapper>
在service层完成对业务层mapper的调用:
package com.example.demo.service.impl;

import com.example.demo.constants.UserConstants;
import com.example.demo.dao.UserMapper;
import com.example.demo.dao.UserRoleMapper;
import com.example.demo.dao.entity.User;
import com.example.demo.service.UserService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.UUID;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;

    @Override
    public User getUser(String name) {
        return userMapper.getUser(name);
    }

    @Override
    public String passwordEncoder(String credentials, String salt) {
        Object object = new SimpleHash("MD5", credentials, salt, UserConstants.HASH_ITERATIONS);
        return object.toString();
    }

    @Override
    public int addUser(User user) {
        user.setSalt(DigestUtils
                .md5Hex(UUID.randomUUID().toString() + System.currentTimeMillis() + UUID.randomUUID().toString()));
        user.setPassword(passwordEncoder(user.getPassword(), user.getSalt()));
        //添加用户
        int i = userMapper.insertSelective(user);
        int j = saveUserRoles(user.getId(), user.getRoleIds());
        if(i!=0 && j!=0){
            return 1;
        }
        return 0;
    }

    private int saveUserRoles(Integer userId, List<String> roleIds) {
        String username = String.valueOf(userId);
        int  i=0;
        if (roleIds != null) {
            userRoleMapper.deleteRoleByUserId(username);
            if (!CollectionUtils.isEmpty(roleIds)) {
                i = userRoleMapper.saveUserRoles(username, roleIds);
            }
        }
        return i;
    }
}
然后就是重点了!我们需要建立两个类MyShiroRelam和shiroConfig。

MyShiroRelam的代码如下:
package com.example.demo.config;

import com.example.demo.dao.PermissionMapper;
import com.example.demo.dao.RoleMapper;
import com.example.demo.dao.entity.Permission;
import com.example.demo.dao.entity.Role;
import com.example.demo.dao.entity.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.SpringUtil;
import com.example.demo.utils.UserUtil;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

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


public class MyShiroRealm extends AuthorizingRealm {
	
	private static final Logger log = LoggerFactory.getLogger("adminLogger");

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
		//根据用户名查询用户信息
		String username = usernamePasswordToken.getUsername();
		UserService userService = SpringUtil.getBean(UserService.class);
		//查询到的用户信息
		User user = userService.getUser(username);
		if (user == null) {
			throw new UnknownAccountException("用户名不存在或密码错误");
		}
		if (!user.getPassword()
				.equals(userService.passwordEncoder(new String(usernamePasswordToken.getPassword()), user.getSalt()))) {
			throw new IncorrectCredentialsException("密码错误");
		}

		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
				ByteSource.Util.bytes(user.getSalt()), getName());

		UserUtil.setUserSession(user);

		return authenticationInfo;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		log.debug("权限配置");

		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		User user = UserUtil.getCurrentUser();
		//TODO 获得用户角色
		List<Role> roles = SpringUtil.getBean(RoleMapper.class).listByUserId(user.getId());
		Set<String> roleNames = roles.stream().map(Role::getRoleName).collect(Collectors.toSet());
		authorizationInfo.setRoles(roleNames);
		//TODO 获得用户权限
		List<Permission> permissionList = SpringUtil.getBean(PermissionMapper.class).listPermissionByUserId(user.getId());
		UserUtil.setPermissionSession(permissionList);
		Set<String> permissions = permissionList.stream().filter(p -> !StringUtils.isEmpty(p.getPermissionName()))
				.map(Permission::getPermissionName).collect(Collectors.toSet());
		authorizationInfo.setStringPermissions(permissions);

		return authorizationInfo;
	}


}

ShiroConfig类的代码如下:
 
package com.example.demo.config;

import com.example.demo.constants.UserConstants;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

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

@Configuration
public class ShiroConfig {

	@Bean
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		shiroFilterFactoryBean.setSecurityManager(securityManager);

		// 拦截器.
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //过滤掉不需要拦截的请求
		//这不需要登录就可以访问的请求
		filterChainDefinitionMap.put("/css/**", "anon");
		filterChainDefinitionMap.put("/fonts/**", "anon");
		filterChainDefinitionMap.put("/img/**", "anon");
		filterChainDefinitionMap.put("/js/**", "anon");
		filterChainDefinitionMap.put("/login", "anon");
		filterChainDefinitionMap.put("/insertUser", "anon");
		filterChainDefinitionMap.put("/files/*", "anon");
		//这登录之后才可以访问的请求
		filterChainDefinitionMap.put("/**", "authc");
		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/login.html");
		//登录成功后到达的页面
		shiroFilterFactoryBean.setSuccessUrl("/index.html");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	@Bean
	public SecurityManager securityManager() {
		EhCacheManager cacheManager = new EhCacheManager();
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//设置realm.
		securityManager.setRealm(myShiroRealm());
		//注入缓存管理器,这个如果执行多次,也是同样的一个对象;
		securityManager.setCacheManager(cacheManager);
		return securityManager;
	}

	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		// 必须设置 SecurityManager
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * 凭证匹配器
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(UserConstants.HASH_ITERATIONS);

		return hashedCredentialsMatcher;
	}

	/**
	 * Shiro生命周期处理器
	 * 
	 * @return
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	/**
	 * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),
	 * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
	 * 
	 * @return
	 */
	@Bean
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}
}


下面是自己定义的util类
package com.example.demo.utils;

import com.example.demo.constants.UserConstants;
import com.example.demo.dao.entity.Permission;
import com.example.demo.dao.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

import java.util.List;

public class UserUtil {

	public static User getCurrentUser() {
		User user = (User) getSession().getAttribute(UserConstants.LOGIN_USER);

		return user;
	}

	public static void setUserSession(User user) {
		getSession().setAttribute(UserConstants.LOGIN_USER, user);
	}

	@SuppressWarnings("unchecked")
	public static List<Permission> getCurrentPermissions() {
		List<Permission> list = (List<Permission>) getSession().getAttribute(UserConstants.USER_PERMISSIONS);

		return list;
	}

	public static void setPermissionSession(List<Permission> list) {
		getSession().setAttribute(UserConstants.USER_PERMISSIONS, list);
	}

	public static Session getSession() {
		Subject currentUser = SecurityUtils.getSubject();
		Session session = currentUser.getSession();

		return session;
	}
}

package com.example.demo.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * spring获取bean工具类
 * 
 *
 */
@Component
public class SpringUtil implements ApplicationContextAware {

	private static ApplicationContext applicationContext = null;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringUtil.applicationContext = applicationContext;
	}

	public static <T> T getBean(Class<T> cla) {
		return applicationContext.getBean(cla);
	}

	public static <T> T getBean(String name, Class<T> cal) {
		return applicationContext.getBean(name, cal);
	}

	public static String getProperty(String key) {
		return applicationContext.getBean(Environment.class).getProperty(key);
	}
}

最后就是controller类的调用
package com.example.demo.controller;

import com.example.demo.dao.entity.User;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


import java.util.HashMap;
import java.util.Map;

/**
 * Created by chendai on 2018/3/10.
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 用户注册
     */
    @PostMapping(value = "/insertUser")
    public Map<String,Object> addUser(@RequestBody User user){
        Map<String,Object> messageMap = new HashMap<>();
        int i = userService.addUser(user);
        if(i !=0){
            messageMap.put("message","success");
        }else{
            messageMap.put("message","failed");
        }
        return messageMap;
    }
    /**
     * 登录
     */
    @PostMapping(value = "login")
    public Map<String,String> login(@RequestParam("username") String username, @RequestParam("password")String password){
      Map<String,String> messageMap = new HashMap<>();
        try{
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
            SecurityUtils.getSubject().login(usernamePasswordToken);
            messageMap.put("message","登录成功");
        }catch (Exception e){
            String message = e.getMessage();
            messageMap.put("message",message);
        }
        return messageMap;
    }
}
这样就完成了springBoot 、myBatis和shiro的整合。
























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值