apache-shiro-(02)

基于SpringBoot的Apache Shiro环境简单搭建

持久层使用的是MyBatis

1. idea中,Create New Project;
2. 选中Spring Initializer项目,选择SDK,下一步;
3. 填写Group、Artifact、Type(MavenProject),打包方式为 war;填写Name、Description、Package;
4.  依赖添加:Core—>lombok;Web—>web;Sql—>mybatis、mysql;下一步;Finish。

目录结构:


5. 引入依赖,pom.xml文件如下配置

<?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 fmappertory -->
	</parent>
	<groupId>com.shiro.syy</groupId>
	<artifactId>apache-shiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>apache-shiro</name>
	<description>spring boot mybatis shiro </description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>2.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

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

		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--连接池-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.10</version>
		</dependency>

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

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

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.4</version>
		</dependency>
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
			<version>3.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.0.6.RELEASE</version>
		</dependency>
		<!--jsp-->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

6. 由于pom.xml中设置了数据库连接池,因此需要先配置数据库连接池,项目才能初始运行成功。application.properties文件如下配置:

## database配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=zxcvbnm123

## mybatis配置。依次配置内容是 mybatis实体映射的xml文件;实体类所在包名;打印数据库查询语句。
mybatis.mapper-locations=mappers/*.xml
mybatis.type-aliases-package=com.shiro.syy.model
logging.level.com.shiro.syy.mapper=debug

## 视图解析配置
spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.jsp

7. 建表

CREATE DATABASE IF NOT EXISTS shiro;
-- 权限表 --
CREATE TABLE permission(
  pid INT (11) NOT NULL AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL DEFAULT '',
  url VARCHAR(255) DEFAULT '',
  PRIMARY KEY (pid)
) ENGINE=InnoDB DEFAULT CHARSET = utf8;
INSERT INTO permission VALUES ('1','add','');
INSERT INTO permission VALUES ('2','delete','');
INSERT INTO permission VALUES ('3','edit','');
INSERT INTO permission VALUES ('4','query','');
-- 用户表 --
CREATE TABLE user(
  uid INT (11) NOT NULL AUTO_INCREMENT,
  username VARCHAR(255) NOT NULL DEFAULT '',
  password VARCHAR(255) NOT NULL DEFAULT '',
  PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET = utf8;

INSERT INTO user VALUES ('1','admin','123');
INSERT INTO user VALUES ('2','demo','123');
-- 角色表 --
CREATE TABLE role(
  rid INT (11) NOT NULL AUTO_INCREMENT,
  rname VARCHAR(255) NOT NULL DEFAULT '',
  PRIMARY KEY (rid)
) ENGINE=InnoDB DEFAULT CHARSET = utf8;
INSERT INTO role VALUE ('1','admin');
INSERT INTO role VALUE ('2','customer');
-- 角色权限表 --
CREATE TABLE permission_role(
  rid INT(11) NOT NULL ,
  pid INT(11) NOT NULL ,
  KEY idx_rid (rid),
  KEY idx_pid (pid)
) ENGINE=InnoDB DEFAULT CHARSET = utf8;
INSERT INTO permission_role VALUE ('1','1');
INSERT INTO permission_role VALUE ('1','2');
INSERT INTO permission_role VALUE ('1','3');
INSERT INTO permission_role VALUE ('1','4');
INSERT INTO permission_role VALUE ('2','1');
INSERT INTO permission_role VALUE ('2','4');
-- 用户角色表 --
CREATE TABLE user_role(
  uid INT(11) NOT NULL ,
  rid INT(11) NOT NULL ,
  KEY idx_rid (uid),
  KEY idx_pid (rid)
) ENGINE=InnoDB DEFAULT CHARSET = utf8;
INSERT INTO user_role VALUE ('1','1');
INSERT INTO user_role VALUE ('2','2');

8. 实体类

@Data
public class User {
    private Integer uid;
    private String username;
    private String password;
    private Set<Role> roles = new HashSet<>();
}
@Data
public class Role {
    private Integer rid;
    private String rname;
    private Set<Permission> permissions = new HashSet<>();
    private Set<User> users = new HashSet<>();
}
@Data
public class Permission {
    private Integer pid;
    private String name;
    private String url;
}

9. 实体类-数据表 映射 配置文件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.shiro.syy.mapper.UserMapper">
    <!--设置User、Role、Permission对应关系-->
    <resultMap id="userMap" type="com.shiro.syy.model.User">
        <id property="uid" column="uid" />
        <result property="username" column="username" />
        <result property="password" column="password" />
        <collection property="roles" ofType="com.shiro.syy.model.Role">
            <id property="rid" column="rid" />
            <result property="rname" column="rname" />
            <collection property="permissions" ofType="com.shiro.syy.model.Permission">
                <id property="pid" column="pid"/>
                <result property="name" column="name" />
                <result property="url" column="url" />
            </collection>
        </collection>
    </resultMap>
    <select id="findByUsername" parameterType="String" resultMap="userMap">
        SELECT u.*,r.*,p.*
        FROM user u
          INNER JOIN user_role ur on ur.uid = u.uid
          INNER JOIN role r on r.rid = ur.rid
          INNER JOIN permission_role pr on pr.rid = r.rid
          INNER JOIN permission p on p.pid = pr.pid
        WHERE u.username = #{username}
    </select>
</mapper>

配置数据持久层

public interface UserMapper {
    User findByUsername(@Param("username") String username);
}

配置业务逻辑层

扫描二维码关注公众号,回复: 6525782 查看本文章
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;
    @Override
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }
}

10. 配置密码校验规则

public class CredentialMatcher extends SimpleCredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //获取当前用户的密码
        UsernamePasswordToken usernamePasswordToken =  (UsernamePasswordToken) token;
        String password = new String(usernamePasswordToken.getPassword());
        //获取数据库中的密码
        String dbPassword = (String)info.getCredentials();
        return this.equals(password,dbPassword);
    }
}

11. 配置用户身份验证和权限验证

public class AuthRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    /**
     * 权限认证,shiro授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //先从session中获取user对象
        User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();
        //从当前用户中获取所有的角色
        List<String> roleNames = new ArrayList<>();
        //从当前用户的角色中获取所有权限
        List<String> permissions = new ArrayList<>();
        Set<Role> roleSet = user.getRoles();
        if(CollectionUtils.isNotEmpty(roleSet)){
            for(Role role : roleSet){
                roleNames.add(role.getRname());
                Set<Permission> permissionSet = role.getPermissions();
                if(CollectionUtils.isNotEmpty(permissionSet)){
                    for(Permission permission:permissionSet){
                        permissions.add(permission.getName());
                    }
                }
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roleNames);   //角色
        info.addStringPermissions(permissions); //权限
        return info;
    }
    /**
     * 身份认证,shiro认证登录
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = userService.findByUsername(username);

        return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
    }
}

12. shiro核心配置器


/**
 * 配置IOC容器的Bean对象
 * shiro核心配置器
 * 将securityManager、realm包中配置好的对象(身份验证器和密码校验器)注入IOC容器中.
 * 当项目启动时,首先初始化ShifroFilter()方法,传入参数SecurityManager进行实例构造,
 *   从而初始化SecurityManager,……,最后是自定义的CredentialMatcher密码校验器初始化。
 */
@Configuration
public class ShiroConfiguration {
    /**
     * 注入ShiroFilter过滤器到IOC容器中
     * @param manager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean siroFilter(@Qualifier("securityManager") SecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);

        // 设置登录页面、登录成功后跳转的页面、登录失败后跳转的页面
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/home");
        bean.setUnauthorizedUrl("/unauthorized");

        // 设置页面的权限过滤器链,并配置到bean中。
        LinkedHashMap<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        //过滤器链有顺序的。
        filterChainDefinitionMap.put("/index","authc"); //指定拦截器,登录才可访问
        filterChainDefinitionMap.put("/login","anon");  //未验证
        filterChainDefinitionMap.put("/loginUser","anon");  //未验证
        filterChainDefinitionMap.put("/admin","roles[admin]");  //角色名:admin
        filterChainDefinitionMap.put("/edit","perms[edit]");    //权限名:edit
        filterChainDefinitionMap.put("/unauthorized","anon");   //未验证
        filterChainDefinitionMap.put("/druid/**","anon");
        filterChainDefinitionMap.put("/**","user");  //是否登录
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }
    /**
     * 注入SecurityManager对象到IOC容器中,
     * 以自定义的验证方式进行用户验证
     * @param authRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        return manager;
    }
    /**
     * 将身份权限验证类注入到IOC容器中
     * @param matcher @Qualifier注解表示,使用spring上下文的matcher对象
     * @return
     */
    @Bean("authRealm")
    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher){
       //注入密码校验器
        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }
    /**
     * 注入密码校验规则类到IOC容器中
     * @return
     */
    @Bean("credentialMatcher")
    public CredentialMatcher credentialMatcher(){
        return new CredentialMatcher();
    }
    /**
     * 以下两个类用于设置spring和shiro框架的关联
     * 配置advisor,使得Spring通过shiro验证时,使用的是自定义的SecurityManager
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
}

13. 配置控制器

@Controller
public class TestController {
    @RequestMapping(value = "/login")
    public String login(){
        return "login";
    }
    @RequestMapping(value = "/index")
    public String index(){
        return "index";
    }
    @RequestMapping(value = "/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        if(subject!=null){
            subject.logout();
        }
        return "login";
    }
    @ResponseBody
    @RequestMapping(value = "/admin")
    public String admin(){
        return "admin success";
    }
    @ResponseBody
    @RequestMapping(value = "/edit")
    public String edit(){
        return "edit success";
    }
    @RequestMapping(value = "/loginUser")
    public String loginUser(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            HttpSession session){
        //获取token令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        //获取主体
        Subject subject = SecurityUtils.getSubject();
        try{
            subject.login(token);
            User user = (User) subject.getPrincipal();

            session.setAttribute("user",user);
            return "index";
        }catch(AuthenticationException e){
            System.out.println(username+":"+password);
            System.out.println("账号密码错误");
            return "login";
        }
    }
    @RequestMapping(value = "/unauthorized")
    public String unauthorized(){ //权限不够
        return "unauthorized";
    }
}

猜你喜欢

转载自blog.csdn.net/J1014329058/article/details/88625793
今日推荐