springboot集成shiro(一)

1.使用intellij新建springboot项目,编写pom文件,各部分的dependency作用都有说明

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<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>3.4</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.17</version>
		</dependency>


		<!--springBoot thymeleaf模板-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>

		<!--shiro-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>

		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.0</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>

		<!--mysql connect-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.20</version>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-juli</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.39</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-commons</artifactId>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.8.9</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.9</version>
		</dependency>

		<!--springBoot configuration-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

2.配置mybatis和mysql

mybatis配置文件

package com.shiro.demo.config;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.Properties;

/**
 * @author tracyclock  2017-12-08
 * springboot集成mybatis的基本入口
 * 1)创建数据源
 * 2)创建SqlSessionFactory
 */
@Configuration
@MapperScan(basePackages="com.shiro.demo.mapper")
public class MyBatisConfig implements EnvironmentAware {

    private Environment env;
    @Override
    public void setEnvironment(Environment environment) {
        this.env = environment;
    }

    /**
     * 创建数据源
     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
     */
    @Bean
    public DataSource getDataSource() throws Exception{
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
        props.put("url", env.getProperty("jdbc.url"));
        props.put("username", env.getProperty("jdbc.username"));
        props.put("password", env.getProperty("jdbc.password"));
        return DruidDataSourceFactory.createDataSource(props);

    }

    /**
     * 根据数据源创建SqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception{
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        //开启驼峰规则映射
        config.setMapUnderscoreToCamelCase(true);
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();

        fb.setConfiguration(config);
        Interceptor[] interceptors = new Interceptor[]{new PagePlugin()};
        fb.setPlugins(interceptors);
        fb.setDataSource(ds);//指定数据源(这个必须有,否则报错)
        //下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
        fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));//指定基包
        fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//指定xml文件位置
        return fb.getObject();
    }
}

PagePlugin  mybatis分页插件

package com.shiro.demo.config;

import com.shiro.demo.domain.Pager;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
 * @author tracyclock 2017-12-08
 * 分页插件
 */
@Intercepts({@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class,Integer.class}
)})
public class PagePlugin implements Interceptor {
    public PagePlugin() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        if (target instanceof StatementHandler) {
            StatementHandler statementHandler = (StatementHandler)target;
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            RowBounds rowBounds = null;

            try {
                rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
                if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
                    return invocation.proceed();
                }
            } catch (Exception var11) {
                invocation.getTarget();
            }

            BoundSql boundSql = (BoundSql)metaStatementHandler.getValue("delegate.boundSql");
            String originalSql = boundSql.getSql();
            if (rowBounds instanceof Pager) {
                MappedStatement mappedStatement = (MappedStatement)metaStatementHandler.getValue("delegate.mappedStatement");
                Connection connection = (Connection)invocation.getArgs()[0];
                Pager page = (Pager)rowBounds;
                page = this.count(originalSql, connection, mappedStatement, boundSql, page);
                if (page.getTotal() <= 0L) {
                    return invocation.proceed();
                }

                originalSql = this.buildPageSql(originalSql, page.getPageNumber(), page.getSize());
            }

            metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
        }

        return invocation.proceed();
    }

    private String buildPageSql(String originalSql, int offset, int limit) {
        StringBuilder sql = new StringBuilder(originalSql);
        sql.append(" LIMIT ").append(offset).append(",").append(limit);
        return sql.toString();
    }

    private Pager count(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Pager page) {
        String sqlUse = sql;
        int order_by = sql.toUpperCase().lastIndexOf("ORDER BY");
        if (order_by > -1) {
            sqlUse = sql.substring(0, order_by);
        }

        StringBuffer countSql = new StringBuffer();
        countSql.append("SELECT COUNT(1) AS TOTAL FROM (").append(sqlUse).append(") A");
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            pstmt = connection.prepareStatement(countSql.toString());
            BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql.toString(), boundSql.getParameterMappings(), boundSql.getParameterObject());
            ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), countBS);
            parameterHandler.setParameters(pstmt);
            rs = pstmt.executeQuery();
            int total = 0;
            if (rs.next()) {
                total = rs.getInt(1);
            }

            page.setTotal((long)total);
        } catch (SQLException var22) {
            var22.printStackTrace();
        } finally {
            try {
                rs.close();
                pstmt.close();
            } catch (SQLException var21) {
                var21.printStackTrace();
            }

        }

        return page;
    }

    public Object plugin(Object target) {
        return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {

    }

}

Pager

package com.shiro.demo.domain;

import org.apache.ibatis.session.RowBounds;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.io.Serializable;

public class Pager<T> extends RowBounds implements Pageable, Serializable {
    private long total;
    private T data;
    private int pageNo = 1; //vision的page是从1开始的
    private int pageSize = 10;

    Pager() {
    }

    public int getPageNo() {
        return pageNo;
    }

    public void setPageNo(int pageNo) {
        this.pageNo = pageNo;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public Pager(int pageNo, int pageSize) {
        this.pageNo = pageNo;
        this.pageSize = pageSize;
    }

    public void setData(T data) {
        this.data = data;
    }

    public T getData() {
        return this.data;
    }

    public long getTotal() {
        return this.total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public int getPage() {
        return this.pageNo;
    }

    public void setPage(int page) {
        this.pageNo = pageNo;
    }

    public int getSize() {
        return this.pageSize;
    }

    public void setSize(int size) {
        this.pageSize = pageSize;
    }

    public int getPageNumber() {
        return (this.pageNo - 1) * this.pageSize;
    }

    public int getPageSize() {
        return this.pageSize;
    }

    public int getOffset() {
        return 0;
    }

    public Sort getSort() {
        return null;
    }

    public Pageable next() {
        return new Pager(this.pageNo + 1, this.pageSize);
    }

    public Pageable previousOrFirst() {
        return null;
    }

    public Pageable first() {
        return null;
    }

    public boolean hasPrevious() {
        return false;
    }

    @Override
    public String toString() {
        return "Pager{" +
                "total=" + total +
                ", data=" + data +
                ", pageNo=" + pageNo +
                ", pageSize=" + pageSize +
                '}';
    }
}



application.yml中配置mysql信息

jdbc.driverClassName: com.mysql.jdbc.Driver
jdbc.url: jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=UTF-8
jdbc.username: root
jdbc.password:
jdbc.initialSize: 20
jdbc.minIdle: 20
jdbc.maxActive: 100

mybatis.typeAliasesPackage: com.shiro.demo.domain
mybatis.mapperLocations: classpath:mybatis-mappers/*.xml

# shiro 配置不会被拦截的路径
# 具体的退出代码Shiro已经替我们实现了
# 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
# authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
shiroFilter:
  filterChainDefinitionMap:
    /shiro/loginAction: anon
    /shiro/index: anon


# 模板配置
# 这个开发配置为false,避免改了模板还要重启服务器
spring.thymeleaf.cache: false
# 这个是配置模板路径的,默认就是templates,可不用配置
spring.thymeleaf.prefix=classpath: /templates/
# 这个可以不配置,检查模板位置
spring.thymeleaf.check-template-location: true
# 下面3个不做解释了,可以不配置
spring.thymeleaf.suffix: .html
spring.thymeleaf.encoding: UTF-8
spring.thymeleaf.content-type: text/html
# 模板的模式
spring.thymeleaf.mode: HTML5


3.编写实体类User,Role,Permission

User

package com.shiro.demo.domain;

import lombok.Data;
import java.io.Serializable;
import java.util.Date;

/**
 * @author tracyclock 2017-12-08
 */

@Data
public class User implements Serializable {
    private Long id;
    private String name;
    private String password;
    private String email;
    private Date createTime;
    private Date updateTime;
    private Date lastLoginTime;
    private Integer status;
    private String salt;
}
Role

package com.shiro.demo.domain;

import lombok.Data;

import java.io.Serializable;
/**
 * @author tracyclock 2017-12-08
 */
@Data
public class Role implements Serializable {
    private Long id;
    private String name;
    private String type;
    private Integer status; // 是否可用,0可用,-1不可用
}
Permission

package com.shiro.demo.domain;

import lombok.Data;
import java.io.Serializable;
/**
 * @author tracyclock 2017-12-08
 */
@Data
public class Permission  implements Serializable{
    private Long id;
    private String url;
    private String name;
    private String resourceType;//资源类型,[menu|button]
    private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
    private Long parentId; //父编号
    private String parentIds; //父编号列表
    private Integer status ;
}




数据库

CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET latin1 */;

USE `test`;


DROP TABLE IF EXISTS `permission`;

CREATE TABLE `permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `url` varchar(256) DEFAULT NULL COMMENT 'url地址',
  `name` varchar(64) DEFAULT NULL COMMENT 'url描述',
  `status` int(11) DEFAULT '0' COMMENT '0 可用  -1删除',
  `parent_id` bigint(20) DEFAULT NULL,
  `parent_ids` varchar(100) DEFAULT NULL,
  `permission` varchar(100) DEFAULT NULL,
  `resource_type` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;


insert  into `permission`(`id`,`url`,`name`,`status`,`parent_id`,`parent_ids`,`permission`,`resource_type`) values (1,'/shiro/list','用户管理',0,0,'0/','shiro:view','menu'),(2,'/shiro/add','用户添加',0,1,'0/1','shiro:add','button'),(3,'/shiro/update','用户更新',0,1,'0/1','shiro:update','button'),(4,'/shiro/userInfo','用户信息',0,0,'0','shiro:view','button');


DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL COMMENT '角色名称',
  `type` varchar(10) DEFAULT NULL COMMENT '角色类型',
  `status` int(11) DEFAULT '0' COMMENT '0可用,-1不可用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


insert  into `role`(`id`,`name`,`type`,`status`) values (1,'admin','管理员',0),(2,'vip','VIP会员',0);


DROP TABLE IF EXISTS `role_permission`;

CREATE TABLE `role_permission` (
  `rid` bigint(20) DEFAULT NULL COMMENT '角色ID',
  `pid` bigint(20) DEFAULT NULL COMMENT '权限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


insert  into `role_permission`(`rid`,`pid`) values (1,3),(1,1),(1,2),(2,4),(1,4);


DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '用户昵称',
  `email` varchar(128) DEFAULT NULL COMMENT '邮箱|登录帐号',
  `password` varchar(32) DEFAULT NULL COMMENT '密码',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
  `status` int(1) DEFAULT '1' COMMENT '1:有效,0:禁止登录',
  `salt` varchar(100) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


insert  into `user`(`id`,`name`,`email`,`password`,`create_time`,`last_login_time`,`status`,`salt`,`update_time`) values (1,'管理员',NULL,'5BB8026FD6A8DC246B2E3F9D42D5EB4B',NULL,'2017-12-09 13:46:37',1,'d3Frh4BgfT','2017-12-09 13:46:37'),(2,'',NULL,NULL,NULL,NULL,1,NULL,NULL);


DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `uid` bigint(20) DEFAULT NULL COMMENT '用户ID',
  `rid` bigint(20) DEFAULT NULL COMMENT '角色ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


insert  into `user_role`(`uid`,`rid`) values (1,1);


4,shiro的配置

主配置文件ShiroConfig

package com.shiro.demo.config;

import com.shiro.demo.domain.Permission;
import com.shiro.demo.service.UserRolePermissionService;
import com.shiro.demo.shiro.MyShiroRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tracyclock 2017-12-08
 * shiro过滤url的配置
 */
@ConfigurationProperties(prefix = "shiroFilter")
@Configuration
public class ShiroConfig{

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserRolePermissionService userRolePermissionService;

    //在application.yml配置文件中配置无需登录验证的url
    private Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();

    public Map<String, String> getFilterChainDefinitionMap() {
        return filterChainDefinitionMap;
    }

    public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
        this.filterChainDefinitionMap = filterChainDefinitionMap;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
        logger.info("ShiroConfig shiroFilter() start~");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //如果不设置默认会自动寻找Web工程根目录下的"/login"页面
        shiroFilterFactoryBean.setLoginUrl("/shiro/login");
        //登录成功后要跳转的链接,备用
        shiroFilterFactoryBean.setSuccessUrl("/shiro/index");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        //filterChainDefinitionMap继续添加需要权限验证的url,可以在类中配置,也能在yml文件配置,这里采用数据库的配置
        //例子:filterChainDefinitionMap.put("/add", "perms[权限添加]");
        //获得数据库需要权限验证的所有信息
        String permissionStr = "perms[%s]";
        List<Permission> permissionList = this.userRolePermissionService.permissionList();
        logger.info("ShiroConfig shiroFilter() permissionList{}",permissionList);
        
        if(permissionList != null && permissionList.size() > 0){
            for (Permission permission : permissionList) {
                filterChainDefinitionMap.put(permission.getUrl(), String.format(permissionStr,permission.getName()));
            }
        }
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

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


    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
}
需自己实现业务逻辑的MyShiroReaml

package com.shiro.demo.shiro;

import com.shiro.demo.domain.Permission;
import com.shiro.demo.domain.Role;
import com.shiro.demo.domain.User;
import com.shiro.demo.service.UserRolePermissionService;
import com.shiro.demo.service.UserService;
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.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author tracyclock 2017-12-08
 * 用于进行权限信息的验证,需要我们自己实现
 */
public class MyShiroRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private UserService userService;
    @Autowired
    private UserRolePermissionService userRolePermissionService;

    /**
     * AuthorizationInfo 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("用户授权-->MyShiroRealm  doGetAuthorizationInfo()  start~");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user  = (User)principalCollection.getPrimaryPrincipal();

        //用户role集合
        List<Role> roleList = userRolePermissionService.roleListByUserId(user.getId());
        if(roleList == null || roleList.size() <= 0){
            return authorizationInfo;
        }
        Set<Long> roleIds = new HashSet<>();
        Set<String> roleSet = new HashSet<>();
        Set<String> permissionSet = new HashSet<>();
        for(Role role : roleList){
            roleSet.add(role.getType());
            roleIds.add(role.getId());
        }
        logger.info("MyShiroRealm  doGetAuthorizationInfo()  roleSet{}",roleSet);
        
        //用户permission集合
        List<Permission> permissionList = this.userRolePermissionService.permissionListByRoleIds(roleIds);
        if(permissionList != null && permissionList.size() > 0){
            for (Permission permission :permissionList) {
                permissionSet.add(permission.getName());
            }
        }
        logger.info("MyShiroRealm  doGetAuthorizationInfo()  permissionSet{}",permissionSet);
        
        //将用户的角色信息,权限信息,添加到authorizationInfo
        //debug可以看到,返回authorizationInfo后,就开始验证权限是否符合,未授权就会跳转之前配置的未授权页面
        authorizationInfo.setRoles(roleSet);
        authorizationInfo.setStringPermissions(permissionSet);
        
        return authorizationInfo;
    }

    /**
     * AuthenticationInfo 是用来验证用户身份,判断登陆是否成功
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("用户验证----->MyShiroRealm   doGetAuthenticationInfo() start~");
        //从token获取用户的输入的账号
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        String name = token.getUsername();//得到用戶名
        //通过name从数据库中查找 User对象
        User user = userService.getUserByName(name);
        
        //此处可用缓存机制实现N点登陆,几次登陆失败后禁止登陆等操作
        if(user == null){
            throw new AccountException("帐号不正确!");
        }else if(user.getStatus().intValue() == 0){
            throw new DisabledAccountException("此帐号已经设置为禁止登录!");
        }
        logger.info("doGetAuthenticationInfo() user{}",user);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());
        return authenticationInfo;
    }


    /**
     * 重写shiro的密码验证方式
     * CustomCredentialsMatcher 自己实现的加盐验证
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        setCredentialsMatcher(new CustomCredentialsMatcher());
    }

}

根据自己业务重写的密码加盐后验证

package com.shiro.demo.shiro;

import com.shiro.demo.utils.MD5Util;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

/**
 * @author tracyclock 2017-12-08
 * shiro自定义密码验证
 */
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
        //传入的参数进行类型转换
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        SimpleAuthenticationInfo simpleInfo = (SimpleAuthenticationInfo)info;
        //获取传入的盐
        String salt = new String(simpleInfo.getCredentialsSalt().getBytes());

        //tokenCredentials,用户输入的密码经过加密后的密码   accountCredentials:传入的数据库密码
        Object tokenCredentials = encrypt(String.valueOf(token.getPassword()),salt);
        Object accountCredentials = getCredentials(info);
        //将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
        return equals(tokenCredentials, accountCredentials);
    }

    //自定义加密方法,将传进来密码用MD5加盐加密
    private String encrypt(String password,String salt) {
        return MD5Util.getMD5CodeBySalt(password,salt);
    }

}
MD5加盐参考 http://blog.csdn.net/tracyclock/article/details/73484401


5.写controller进行验证

@Controller
@RequestMapping("/shiro")
public class ShiroController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserService userService;

    @RequestMapping("/index")
    public String index(){
        logger.info("go index");
        return "index";
    }

    @RequestMapping("/login")
    public String login(){
        logger.info("go login");
        return "login";
    }
    @RequestMapping("/add")
    public String add(){
        logger.info("go add");
        return "add";
    }
    @RequestMapping("/update")
    public String update(){
        logger.info("go update");
        return "update";
    }

    @RequestMapping(value = "/loginAction",method = RequestMethod.POST)
    public String loginAction(@RequestParam("name") String name, @RequestParam("password") String password){
        logger.info("go loginAction");
        Map<String,String> map = new HashMap<>();
        UsernamePasswordToken token = new UsernamePasswordToken(name,password.toCharArray());
        try {
            //将用户的登陆交给shiro去验证,登陆失败就重定向到登录页
            SecurityUtils.getSubject().login(token);
        } catch (AuthenticationException e) {
           logger.error("login failed,exception is {}",e.getMessage());
            return "redirect:/shiro/login";
        }
        //验证通过后,更新最近登陆时间
        User user = (User)SecurityUtils.getSubject().getPrincipal();
        user.setLastLoginTime(new Date());
        userService.updateUser(user);
        return "redirect:/shiro/userInfo";
    }

    @RequestMapping(value = "/userInfo")
    public String userInfo(ModelMap modelMap){
        //通过验证后,可以直接取到user对象
        User user = (User)SecurityUtils.getSubject().getPrincipal();
        modelMap.put("user",user);
        return "userInfo";
    }

    /**
     * shiro直接退出登陆
     */
    @RequestMapping(value="logout")
    public String logout(){
        try {
            SecurityUtils.getSubject().logout();
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return "redirect:/shiro/index";
    }

}

附一张目录结构




项目git地址:https://gitee.com/jiaobai/shiro_demo




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值