您的位置:首页 > 编程语言 > Java开发

springBoot shiro整合

2018-03-12 15:45 561 查看
Shiro是一个权限框架,他提供了很方便的权限认证和登录的功能。而springboot必然提供了和shiro整合的功能,接下来就用springboot结合mybatis整合shiro。
Step1. 准备数据库表 这里主要涉及五张表:用户表(user),角色表(role),权限表(permission),用户-角色表(user_role),角色-权限表 (role_permission)。   注意:用户和角色是多对多的关系,角色和权限是多对多的关系。
   用户表的字段有:id(主键),username(用户名),password(密码),salt(在密码加密的时候会用到)。   角色表的字段有:id(主键),rolename(角色名称)。   权限表的字段有:id(主键),permissionname(权限名称)。   用户
4000
-角色表的字段有: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
e80e
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的整合。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: