Springboot整合shiro做安全框架(一)

shiro是开源安全框架,提供身份认证,授权,会话管理,加密等操作

会话管理:

传统项目是cookie维持会话管理,

前后分离开发是获取shiro的sessionId维持会话,参考:https://blog.csdn.net/u013615903/article/details/78781166/

本此整合默认用cookie管理,参考:https://www.cnblogs.com/asker009/p/9471712.html

1、项目结构

Springboot整合shiro做安全框架(一)

2、引入依赖,依赖涉及了JPA,mybatis、shiro、swagger等,做测试需要的依赖,贴上去

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo-1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>demo-1</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<fastjson.version>1.2.37</fastjson.version>
	</properties>

	<dependencies>
	<!-- jpa -->
		<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>  
	        <exclusions><!-- 去掉默认配置 -->  
	            <exclusion>  
	                <groupId>org.springframework.boot</groupId>  
	                <artifactId>spring-boot-starter-logging</artifactId>  
	            </exclusion>  
	        </exclusions>  
	    </dependency> 
	     
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-core</artifactId>
		</dependency>
	
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
			
		<!-- shiro -->
		<dependency>
		   <groupId>org.apache.shiro</groupId>
		   <artifactId>shiro-spring</artifactId>
		   <version>1.4.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- 添加servlet依赖模块 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- 热部署 -->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-devtools</artifactId>
	        <optional>true</optional>
	    </dependency>
	    <!-- 热部署结束 -->
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>
        <!-- 添加jstl标签库依赖模块 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!--添加tomcat依赖模块.-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--  redis-->
          <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 
         <!-- mybaits -->
        <!--<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.0</version>
        </dependency> -->
        <!-- 使用jsp引擎,springboot内置tomcat没有此依赖 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
		 <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-hibernate5</artifactId>
        </dependency>
		<!-- caching -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-cli</groupId>
            <artifactId>commons-cli</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
		    <groupId>org.mybatis.spring.boot</groupId>
		    <artifactId>mybatis-spring-boot-starter</artifactId>
		    <version>1.1.1</version>
		</dependency>
	    
	    <dependency> <!-- 引入log4j2依赖 -->  
	        <groupId>org.springframework.boot</groupId>  
	        <artifactId>spring-boot-starter-log4j2</artifactId>  
	    </dependency>  
	    <dependency><!-- swagger依赖 -->
			<groupId>com.spring4all</groupId>
			<artifactId>spring-boot-starter-swagger</artifactId>
			<version>1.5.0.RELEASE</version>
		</dependency>
		<!-- <dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.6.1</version>
		</dependency>

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.6.1</version>
		</dependency> -->
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
	</dependencies>

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

</project>

3、yml文件

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        platform: mysql
        #url: jdbc:mysql://192.168.1.183:20001/dna?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSl=false
        url: jdbc:mysql://127.0.0.1:3306/ly?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSl=false
        username: root
        password: 123456
        initialSize: 10
        minIdle: 10
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        filters: stat,wall,log4j2
        logSlowSql: true
    jpa:
      hibernate:
        ddl-auto: update
        naming:
          physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl #按字段名字建表
        show-sql: true
        database: mysql
        database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    http:
        encoding:
                charset: utf-8
                enabled: true
                force: true
    cache:
      type: ehcache
      ehcache:
        config: classpath:ehcache.xml
    redis:
     host: 127.0.0.1
     port: 6379
     timeout: 20000
#    集群环境打开下面注释,单机不需要打开
#    cluster:
#      集群信息
#      nodes: xxx.xxx.xxx.xxx:xxxx,xxx.xxx.xxx.xxx:xxxx,xxx.xxx.xxx.xxx:xxxx
#      #默认值是5 一般当此值设置过大时,容易报:Too many Cluster redirections
#      maxRedirects: 3
    pool:
      max-active: 8
      min-idle: 0
      max-idle: 8
      max-wait: -1
      password:
      jpa:
      #按字段名字建表

#mybatis是独立节点,需要单独配置
mybatis:
  typeAliasesPackage: com.example.ehcache.*
  mapperLocations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

server:
  port: 1234
  session-timeout: 30
  tomcat.max-threads: 0
  tomcat.uri-encoding: UTF-8
  servlet:
     context-path: /test

logging:
  config: classpath:log4j2-spring.xml
  #redis集群
  #热部署生效
  #设置重启的目录
  devtools:
    restart:
      enabled:true
      additional-paths:src/main/java

4、配置自定义角色类

package com.example.demo.shiro.role;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;

import com.example.demo.shiro.entity.SysPermission;
import com.example.demo.shiro.entity.SysRole;
import com.example.demo.shiro.entity.Users;
import com.example.demo.shiro.service.UsersService;

import lombok.extern.log4j.Log4j2;
/**
 * 
 *  @desc shiro核心配置类
 *	@author luobw
 * 
 *  2019年4月29日
 */
@Log4j2
public class MyShiroRealm extends AuthorizingRealm{

	@Autowired
	UsersService usersService;
	
	//权限信息,包括角色以及权限
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		log.info("-----权限信息------->");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		Users user = (Users) principals.getPrimaryPrincipal();
		for (SysRole role : user.getRoleList()) {//获取角色
			authorizationInfo.addRole(role.getRole());
			for (SysPermission permission : role.getPermissions()) {//获取权限
				authorizationInfo.addStringPermission(permission.getPermission());
			}
		}
		return authorizationInfo;
	}
	/*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		log.info("-----身份验证------->");
		String userName = (String) token.getPrincipal();
		log.info("---------->"+token.getCredentials());
		Users users = usersService.findByUserName(userName);
		log.info("-------->"+users);
		
		if(users == null) {
			return null;
		}
		//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo
				(users, //这里传入的是user对象,比对的是用户名,直接传入用户名也没错,但是在授权部分就需要自己重新从数据库里取权限
						users.getPassword(), 
						ByteSource.Util.bytes(users.getCredentialsSalt()),getName());
		return simpleAuthenticationInfo;
	}

}

5、配置shiro自定义加载类

package com.example.demo.shiro.config;

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import com.example.demo.shiro.role.MyShiroRealm;

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

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
/**
 * 
 *  @desc shiro配置
 *	@author luobw
 * 
 *  2019年4月29日
 */
@Configuration
public class ShiroConfig {
   @Bean
   public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
      System.out.println("ShiroConfiguration.shirFilter()");
      ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
      shiroFilterFactoryBean.setSecurityManager(securityManager);
      //拦截器.
      Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
      // 配置不会被拦截的链接 顺序判断,因为前端模板采用了thymeleaf,这里不能直接使用 ("/static/**", "anon")来配置匿名访问,必须配置到每个静态目录
      filterChainDefinitionMap.put("/css/**", "anon");
      filterChainDefinitionMap.put("/fonts/**", "anon");
      filterChainDefinitionMap.put("/img/**", "anon");
      filterChainDefinitionMap.put("/js/**", "anon");
      filterChainDefinitionMap.put("/html/**", "anon");
      //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
      filterChainDefinitionMap.put("/logout", "logout");
      filterChainDefinitionMap.put("/login", "anon");
      //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
      //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
      filterChainDefinitionMap.put("/**", "authc");
      // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
      //传统项目中,登出后应重定向请求,到登录界面或其他指定界面,在前后端分离的项目中,我们应该返回json信息。在上面提到的ShiroConfig中配置了默认登录路由
      shiroFilterFactoryBean.setLoginUrl("/unauth");
      // 登录成功后要跳转的链接
      shiroFilterFactoryBean.setSuccessUrl("/index");

      //未授权界面;
      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(2);//散列的次数,比如散列两次,相当于 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());
      return securityManager;
   }

   /**
    *  开启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","/user/403");
      r.setExceptionMappings(mappings);  // None by default
      r.setDefaultErrorView("error");    // No default
      r.setExceptionAttribute("exception");     // Default is "exception"
      //r.setWarnLogCategory("example.MvcLogger");     // No default
      return r;
   }
}

6、涉及到的用户表、角色表、权限表

用户实体类

package com.example.demo.shiro.entity;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.format.annotation.DateTimeFormat;


@Entity
@Table(name = "users")
public class Users {
    @Id
    @GenericGenerator(name="generator",strategy = "native")
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer userId;
    @Column(nullable = false, unique = true)
    private String userName; //登录用户名
    @Column(nullable = false)
    private String name;//名称(昵称或者真实姓名,根据实际情况定义)
    @Column(nullable = false)
    private String password;
    private String salt;//加密密码的盐
    private byte state;//用户状态,0:创建未认证(比如没有**,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定.
    @ManyToMany(fetch= FetchType.EAGER)//立即从数据库中进行加载数据;
    @JoinTable(name = "SysUserRole", 
    joinColumns = {
    		@JoinColumn(name = "userId")}, 
    inverseJoinColumns ={
    		@JoinColumn(name = "roleId")})
    private List<SysRole> roleList;// 一个用户具有多个角色
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
    private LocalDateTime createTime;//创建时间
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate expiredDate;//过期日期
    private String email;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDate getExpiredDate() {
        return expiredDate;
    }

    public void setExpiredDate(LocalDate expiredDate) {
        this.expiredDate = expiredDate;
    }
   
    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public byte getState() {
        return state;
    }

    public void setState(byte state) {
        this.state = state;
    }

    public List<SysRole> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<SysRole> roleList) {
        this.roleList = roleList;
    }

    /**
     * 密码盐. 重新对盐重新进行了定义,用户名+salt,这样就不容易被**,可以采用多种方式定义加盐
     * @return
     */
    public String getCredentialsSalt(){
        return this.userName+this.salt;
    }

}

角色实体类

package com.example.demo.shiro.entity;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "sysrole")
public class SysRole {
    @Id
    @GenericGenerator(name="generator",strategy = "native")
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer roleId; // 编号
    @Column(nullable = false, unique = true)
    private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的:
    private String description; // 角色描述,UI界面显示使用
    private Boolean available = Boolean.TRUE; // 是否可用,如果不可用将不会添加给用户

    //角色 -- 权限关系:多对多关系;
    @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="userId")})
    private List<Users> users;// 一个角色对应多个用户

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Boolean getAvailable() {
        return available;
    }

    public void setAvailable(Boolean available) {
        this.available = available;
    }

    public List<SysPermission> getPermissions() {
        return permissions;
    }

    public void setPermissions(List<SysPermission> permissions) {
        this.permissions = permissions;
    }

    public List<Users> getUsers() {
        return users;
    }

    public void setUsers(List<Users> users) {
        this.users = users;
    }
}

权限实体类

package com.example.demo.shiro.entity;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "syspermission")
public class SysPermission {
    @Id
    @GenericGenerator(name="generator",strategy = "native")
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer permissionId;//主键.
    @Column(nullable = false)
    private String permissionName;//名称.
    @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.TRUE;
    //角色 -- 权限关系:多对多关系;
    @ManyToMany
    @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})
    private List<SysRole> roles;

    public Integer getPermissionId() {
        return permissionId;
    }

    public void setPermissionId(Integer permissionId) {
        this.permissionId = permissionId;
    }

    public String getPermissionName() {
        return permissionName;
    }

    public void setPermissionName(String permissionName) {
        this.permissionName = permissionName;
    }

    public String getResourceType() {
        return resourceType;
    }

    public void setResourceType(String resourceType) {
        this.resourceType = resourceType;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public String getParentIds() {
        return parentIds;
    }

    public void setParentIds(String parentIds) {
        this.parentIds = parentIds;
    }

    public Boolean getAvailable() {
        return available;
    }

    public void setAvailable(Boolean available) {
        this.available = available;
    }

    public List<SysRole> getRoles() {
        return roles;
    }

    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }
}

7、用户业务类,登录业务类

登录业务类

package com.example.demo.shiro.service;

import com.example.demo.shiro.entity.LoginResult;

public interface LoginService {
	LoginResult login(String userName,String password);
    void logout();

}

用户业务类

package com.example.demo.shiro.service;

import com.example.demo.shiro.entity.Users;

/**
 * 
 *  @desc
 *	@author luobw
 * 
 *  2019年4月29日
 */
public interface UsersService {
	Users findByUserName(String userName);
}

登录实现类

package com.example.demo.shiro.service.impl;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;

import com.example.demo.shiro.entity.LoginResult;
import com.example.demo.shiro.service.LoginService;
/**
 * 
 *  @desc  登录业务处理
 *	@author luobw
 * 
 *  2019年4月29日
 */
@Service
public class LoginServiceImpl implements LoginService {

	/**
	 * 登录
	 */
	@Override
	public LoginResult login(String userName, String password) {
		LoginResult loginResult = new LoginResult();
		if(userName==null || userName.isEmpty()) {
			loginResult.setLogin(false);
			loginResult.setResult("用户名不能为空");
			return loginResult;
		}
		String msg = "";
		//获取subject实例对象
		Subject subject = SecurityUtils.getSubject();
		//将用户名和密码封装到UsernamePasswordToken
		UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
		//认证
		try {
			subject.login(token);
			Session session = subject.getSession();
			session.setAttribute("userName", userName);
			loginResult.setLogin(true);
			return loginResult;
		} catch (UnknownAccountException e) {
			e.printStackTrace();
			 msg = "UnknownAccountException -- > 账号不存在:";
		} catch (IncorrectCredentialsException  e) {
			e.printStackTrace();
			 msg = "IncorrectCredentialsException -- > 密码不正确:";
		}catch (AuthenticationException e) {
            e.printStackTrace();
            msg="用户验证失败";
        }
		loginResult.setLogin(true);
		loginResult.setResult(msg);
		return loginResult;
	}
	/**
	 * 退出
	 */
	@Override
	public void logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();

	}

}

用户实现类

package com.example.demo.shiro.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.shiro.entity.Users;
import com.example.demo.shiro.repository.UserRepository;
import com.example.demo.shiro.service.UsersService;

@Service
public class ServiceImpl implements UsersService {

	@Autowired
	UserRepository userRepository;
	
	@Override
	public Users findByUserName(String userName) {
		return userRepository.findByUserName(userName);
	}

}

登录控制器类

package com.example.demo.shiro.controller;

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

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.shiro.entity.LoginResult;
import com.example.demo.shiro.service.LoginService;

@RestController
public class MyLoginController {
	@Resource
	private LoginService loginService;
	LoginResult loginResult = new LoginResult();

	@RequestMapping({ "/", "/index" })
	public String index() {
		return "/index";
	}

	@RequestMapping("/403")
	public LoginResult unauthorizedRole() {
		System.out.println("------没有权限-------");
		loginResult.setResult("没有权限");
		return loginResult;
	}

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public LoginResult login(Map<String, Object> map, HttpServletRequest request) throws Exception {
		System.out.println("login()");
		String userName = request.getParameter("userName");
		String password = request.getParameter("password");

		LoginResult loginResult = loginService.login(userName, password);
		if (loginResult.isLogin()) {
			return loginResult;
		} else {
			map.put("msg", loginResult.getResult());
			map.put("userName", userName);
			loginResult.setMap(map);
			return loginResult;
		}
	}

	@RequestMapping("/logout")
	public LoginResult logOut(HttpSession session) {
		loginService.logout();
		loginResult.setResult("退出成功");
		return loginResult;
	}

	   /**
			 * 未登录,shiro应重定向到登录界面,此处返回未登录状态信息由前端控制跳转页面
			 * 
			 * @return
			 */
				@RequestMapping(value = "/unauth")
				public LoginResult unauth() {
				 Map<String, Object> map = new HashMap<String, Object>();
				 map.put("code", "1000000");
				 map.put("msg", "未登录");
				 loginResult.setMap(map);
				 return loginResult;
				}
				@RequestMapping(value = "/auth")
				public LoginResult auth() {
				 Map<String, Object> map = new HashMap<String, Object>();
				 map.put("code", "200");
				 map.put("msg", "登录");
				 loginResult.setMap(map);
				 return loginResult;
				}
				@RequestMapping(value = "/user/403")
				public LoginResult exce() {
				 Map<String, Object> map = new HashMap<String, Object>();
				 map.put("code", "500");
				 map.put("msg", "数据异常");
				 loginResult.setMap(map);
				 return loginResult;
				}
	/*
	@RequestMapping(value = "/error")
	public LoginResult error() {
	 Map<String, Object> map = new HashMap<String, Object>();
	 map.put("code", "500");
	 map.put("msg", "error异常");
	 loginResult.setMap(map);
	 return loginResult;
	}*/

}

用户控制器类

package com.example.demo.shiro.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.shiro.entity.LoginResult;

@RestController
@RequestMapping("/user")
public class UserController {
	 LoginResult loginResult = new LoginResult();
	/**
     * 用户查询.
     * @return
     */
    @RequestMapping(value = "/userList",method=RequestMethod.GET)
    @RequiresPermissions("user:view")//权限管理;
    public LoginResult userInfo(){
    	loginResult.setLogin(true);
    	loginResult.setResult("用户查询");
        return loginResult;
    }

    /**
     * 用户添加;
     * @return
     */
    @RequestMapping("/userAdd")
    @RequiresPermissions("user:add")//权限管理;
    public LoginResult userInfoAdd(){
    	loginResult.setLogin(true);
    	loginResult.setResult("用户添加");
        return loginResult;
    }

    /**
     * 用户删除;
     * @return
     */
    @RequestMapping("/userDel")
    @RequiresPermissions("user:del")//权限管理;
    public LoginResult userDel(){
    	loginResult.setLogin(true);
    	loginResult.setResult("用户删除");
        return loginResult;
    }
}

返回值类

package com.example.demo.shiro.entity;

import java.util.Map;

public class LoginResult {
    private boolean isLogin = false;
    private String result;

    private Map<String, Object> map;
    
    
    public Map<String, Object> getMap() {
		return map;
	}

	public void setMap(Map<String, Object> map) {
		this.map = map;
	}

	public boolean isLogin() {
        return isLogin;
    }

    public void setLogin(boolean login) {
        isLogin = login;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }
}

启动项目自动创建表结构,需导入sql数据

INSERT INTO `users` (`userId`,`username`,`name`,`password`,`salt`,`state`)
VALUES ('1', 'admin', '管理员', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0);

INSERT INTO `syspermission` (`permissionId`,`available`,`permissionname`,`parentid`,`parentids`,`permission`,`resourcetype`,`url`)
VALUES (1,0,'用户管理',0,'0/','user:view','menu','user/userList');
INSERT INTO `syspermission` (`permissionId`,`available`,`permissionname`,`parentid`,`parentids`,`permission`,`resourcetype`,`url`)
VALUES (2,0,'用户添加',1,'0/1','user:add','button','user/userAdd');
INSERT INTO `syspermission` (`permissionId`,`available`,`permissionname`,`parentid`,`parentids`,`permission`,`resourcetype`,`url`)
VALUES (3,0,'用户删除',1,'0/1','user:del','button','user/userDel');

INSERT INTO `sysrole` (`roleid`,`available`,`description`,`role`) VALUES (1,0,'管理员','admin');
INSERT INTO `sysrole` (`roleid`,`available`,`description`,`role`) VALUES (2,0,'VIP会员','vip');
INSERT INTO `sysrole` (`roleid`,`available`,`description`,`role`) VALUES (3,1,'test','test');

INSERT INTO `sysrolepermission` (`permissionid`,`roleid`) VALUES (1,1);
INSERT INTO `sysrolepermission` (`permissionid`,`roleid`) VALUES (2,1);
INSERT INTO `sysrolepermission` (`permissionid`,`roleid`) VALUES (3,2);

INSERT INTO `sysuserrole` (`roleid`,`userId`) VALUES (1,1);

7、运行调试

   1.未登录访问用户列表

get  192.168.1.63:1234/test/user/userList

Springboot整合shiro做安全框架(一)

2、登录请求

 post   192.168.1.63:1234/test/login?userName=admin&password=123456 

Springboot整合shiro做安全框架(一)

3、再次访问用户列表

 Springboot整合shiro做安全框架(一)

4、退出请求

get  192.168.1.63:1234/test/logout

Springboot整合shiro做安全框架(一)