springBoot整合shiro實現權限控制

1.pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>
<!--權限控制-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- 頁面使用shiro標籤依賴 -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>1.2.1</version>
        </dependency>

2.config

package com.xlt.xfzb.web.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.xlt.xfzb.web.security.UserRealm;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @Classname ShiroConfiguration
 * @Description 權限控制設置
 * @Date 2020/3/27 17:47
 * @Created by xm
 */
@Configuration
public class ShiroConfiguration {

    // 創建自定義 realm
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }

    // 創建 SecurityManager 對象
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        return securityManager;
    }

    // Filter工廠,設置對應的過濾條件和跳轉條件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        /**
         * anon:匿名用戶可訪問
         * authc:認證用戶可訪問
         * user:使用rememberMe可訪問
         * perms:對應權限可訪問
         * role:對應角色權限可訪問
         */
        Map<String, String> map = new HashMap<>();
        // 開放登錄接口
        map.put("/login", "anon");
        map.put("/error", "anon");
        map.put("/img/**","anon");
        map.put("/css/**","anon");
        map.put("/lib/**","anon");
        map.put("/js/**","anon");
        // 對所有用戶認證
        map.put("/**", "authc");
        // 登出
        map.put("/logout", "logout");
        // 登錄
        // 注意:這裏配置的 /login 是指到 @RequestMapping(value="/login")中的 /login
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 首頁
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 錯誤頁面,認證不通過跳轉
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/unAuth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    // 加入註解的使用,不加這個,註解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    // 跟上面的註解配置搭配使用,有時候加了上面的配置後註解不生效,需要加入下面的配置
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }

    //頁面標籤對象
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

3.依賴實體

用戶

package com.xlt.xfzb.web.entity;

import java.util.HashSet;
import java.util.Set;

/**
 * @Classname User
 * @Description 權限測試用戶
 * @Date 2020/3/27 17:44
 * @Created by xm
 */
public class UserEntity {

    private String id;

    private String name;

    private String password;
    //賬號
    private String account;
    //性別
    private String sex;
    //部門id
    private String deptId;
    //工號
    private String work;
    //自定義集合
    private Set<RoleEntity> roles = new HashSet<RoleEntity>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    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 Set<RoleEntity> getRoles() {
        return roles;
    }

    public void setRoles(Set<RoleEntity> roles) {
        this.roles = roles;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getDeptId() {
        return deptId;
    }

    public void setDeptId(String deptId) {
        this.deptId = deptId;
    }

    public String getWork() {
        return work;
    }

    public void setWork(String work) {
        this.work = work;
    }
}

角色

package com.xlt.xfzb.web.entity;

import java.util.HashSet;
import java.util.Set;

/**
 * @Classname Role
 * @Description TODO
 * @Date 2020/3/30 11:42
 * @Created by xm
 */
public class RoleEntity {

    private String id;
    //姓名
    private String name;
    //備註
    private String remark;
    //自定義菜單實體
    private Set<PermissionEntity> permissions = new HashSet<>();//一個角色有多個權限

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Set<PermissionEntity> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<PermissionEntity> permissions) {
        this.permissions = permissions;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
}

權限

package com.xlt.xfzb.web.entity;

/**
 * @Classname Permission
 * @Description 權限實體
 * @Date 2020/3/30 11:41
 * @Created by xm
 */
public class PermissionEntity {

    private String id;

    private String name;
    //路徑
    private String url;
    //描述
    private String descrirtion;
    //父id
    private String pid;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getUrl() {
        return url;
    }

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

    public String getDescrirtion() {
        return descrirtion;
    }

    public void setDescrirtion(String descrirtion) {
        this.descrirtion = descrirtion;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }
}

4.自定義異常

package com.xlt.xfzb.web.security;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * @Classname NoPermissionException
 * @Description TODO
 * @Date 2020/3/31 11:16
 * @Created by xm
 */
@ControllerAdvice
public class NoPermissionException {

    // 授權失敗,就是說沒有該權限
    @ExceptionHandler(UnauthorizedException.class)
    public String handleShiroException(Exception ex) {
        return "/error/unAuth";
    }

    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public String AuthorizationException(Exception ex) {
        return "權限認證失敗";
    }
}

5.權限認證

package com.xlt.xfzb.web.security;

import com.netflix.discovery.provider.Serializer;
import com.xlt.xfzb.web.entity.PermissionEntity;
import com.xlt.xfzb.web.entity.RoleEntity;
import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.service.UserService;
import org.apache.shiro.SecurityUtils;
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.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname UserRealm
 * @Description TODO
 * @Date 2020/3/31 11:02
 * @Created by xm
 */
@Service
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 用戶授權
     **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principalCollection) {

        System.out.println("===執行授權===");

        Subject subject = SecurityUtils.getSubject();
        UserEntity user = (UserEntity)subject.getPrincipal();
        if(user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 角色字符串集合
            Collection<String> rolesCollection = new HashSet<>();
            // 權限字符串集合
            Collection<String> premissionCollection = new HashSet<>();
            // 讀取並賦值用戶角色與權限
            Set<RoleEntity> roles = user.getRoles();
            for(RoleEntity role : roles){
                rolesCollection.add(role.getName());
                Set<PermissionEntity> permissions = role.getPermissions();
                for (PermissionEntity permission : permissions){
                    // 權限名稱爲PermissionEntity爲字段url
                    premissionCollection.add(permission.getUrl());
                }
                info.addStringPermissions(premissionCollection);
            }
            info.addRoles(rolesCollection);
            return info;
        }
        return null;
    }

    /**
     * 用戶認證
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {

        System.out.println("===執行認證===");

        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        UserEntity bean = userService.findByName(token.getUsername());

        if(bean == null){
            // 用戶不存在
            throw new UnknownAccountException();
        } else {
            bean = userService.findById(bean.getId());
            if(null == bean) {
                // 認證失敗
                throw new AuthenticationException();
            }
        }

        ByteSource credentialsSalt = ByteSource.Util.bytes(bean.getName());

        return new SimpleAuthenticationInfo(bean, bean.getPassword(),
                credentialsSalt, getName());

    }
}

6.持久層

package com.xlt.xfzb.web.mapper;

import com.xlt.xfzb.web.entity.UserEntity;
import org.springframework.stereotype.Service;

/**
 * @Classname UserMapper
 * @Description 用戶測試
 * @Date 2020/3/31 10:45
 * @Created by xm
 */
@Service
public interface UserMapper {

    // 根據用戶名稱,查詢用戶信息
     UserEntity findByName(String name);

    // 根據用戶id,查詢用戶信息、角色、權限
     UserEntity findById(String id);
}
<?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.xlt.xfzb.web.mapper.UserMapper">

    <resultMap id="userMap" type="com.xlt.xfzb.web.entity.UserEntity">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="password" column="password"/>
        <collection property="roles" ofType="com.xlt.xfzb.web.entity.RoleEntity">
            <id property="id" column="roleId"/>
            <result property="name" column="roleName"/>
            <collection property="permissions" ofType="com.xlt.xfzb.web.entity.PermissionEntity">
                <id property="id" column="permissionId"/>
                <result property="name" column="permissionName"/>
                <result property="url" column="permissionUrl"/>
            </collection>
        </collection>
    </resultMap>

    <select id="findByName" parameterType="java.lang.String" resultType="com.xlt.xfzb.web.entity.UserEntity">
      SELECT "id", "name", "password"
        FROM "user"
          WHERE "name" = #{name}
    </select>

    <select id="findById" parameterType="java.lang.String" resultMap="userMap">
      SELECT u."id", u."name", u."password",
            r."id" as roleId, r."name" as roleName,
            p."id" as permissionId,
            p."name" as permissionName,
            p."url" as permissionUrl
        FROM "user" u , "user_role" ur, "role" r, "role_permission" rp, "permission" p
          WHERE u."id" = #{id}
            AND u."id" = ur."user_id"
            AND ur."role_id" = r."id"
            AND r."id" = rp."role_id"
            AND rp."permission_id" = p."id"
    </select>

</mapper>

7.service

package com.xlt.xfzb.web.service;

import com.xlt.xfzb.web.entity.UserEntity;

/**
 * @Classname UserService
 * @Description 用戶相關業務接口
 * @Date 2020/3/30 11:48
 * @Created by xm
 */
public interface UserService {

    /**
     * 根據用戶名稱查詢用戶信息
     * @param name
     * @return
     */
    UserEntity findByName(String name);

    /**
     * 根據用戶ID查詢用戶角色及權限
     * @param id
     * @return
     */
    UserEntity findById(String id);
}
package com.xlt.xfzb.web.service.impl;

import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.mapper.UserMapper;
import com.xlt.xfzb.web.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Classname UserService
 * @Description 用戶相關業務接口實現
 * @Date 2020/3/30 11:48
 * @Created by xm
 */
@Service
public class UserServiceImpl implements UserService {

    //測試用戶持久層
    @Autowired
    private UserMapper userMapper;

    /**
     * 根據用戶名稱查詢用戶信息
     * @param name
     * @return
     */
    @Override
    public UserEntity findByName(String name) {
        return userMapper.findByName(name);
    }

    /**
     * 根據用戶ID查詢用戶角色及權限
     * @param id
     * @return
     */
    @Override
    public UserEntity findById(String id) {
        return userMapper.findById(id);
    }
}

8.controller

package com.xlt.xfzb.web.controller;


import com.xlt.xfzb.web.entity.UserEntity;
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.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @Classname MainController
 * @Description 主頁驗證前端控制器
 * @Date 2020/3/31 11:18
 * @Created by xm
 */
@Controller
public class MainController {

    private static final Logger log=LoggerFactory.getLogger(MainController.class);

    /**
     * 主頁跳轉
     * @param model 攜帶當前登錄用戶信息返回
     * @return
     */
    @RequestMapping("/index")
    public String index(Model model){
        UserEntity user = (UserEntity) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("uid",user.getId());
        model.addAttribute("uname",user.getName());
        log.info("當前登錄用戶-----"+user.getName());
        return "index";
    }

    /**
     * 登錄接口+登錄頁面
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response){
        response.setHeader("root", request.getContextPath());
        String userName = request.getParameter("username");
        String password = request.getParameter("password");
        // 等於null說明用戶沒有登錄,只是攔截所有請求到這裏,那就直接讓用戶去登錄頁面,就不認證了。
        // 如果這裏不處理,那個會返回用戶名不存在,邏輯上不合理,用戶還沒登錄怎麼就用戶名不存在?
        if(null == userName) {
            return "login";
        }

        // 1.獲取Subject
        Subject subject = SecurityUtils.getSubject();
        // 2.封裝用戶數據
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        // 3.執行登錄方法
        try{
            subject.login(token);
            return "redirect:/index";
        } catch (UnknownAccountException e){
            // 這裏是捕獲自定義Realm的用戶名不存在異常
            log.info("用戶名不存在!");
        } catch (IncorrectCredentialsException e){
            log.info("密碼錯誤!");
        } catch (AuthenticationException e) {
            log.info("認證失敗!");
        }

        return "login";
    }

    /**
     * 用戶登出操作
     * @return
     */
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        return "login";
    }

    /**
     * 錯誤頁面
     * @return
     */
    @RequestMapping("/error/unAuth")
    public String unAuth(){
        return "/error/unAuth";
    }

    @RequestMapping("/err")
    public String err(){
        return "/error/unAuth";
    }


}

9.登錄頁面 login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄</title>
</head>
<body>
<h1>用戶登錄</h1>
<hr>
<form id="from" action="/login" method="post">
    <table>
        <tr>
            <td>用戶名</td>
            <td>
                <input type="text" name="username" placeholder="請輸入賬戶名" value="" />
            </td>
        </tr>
        <tr>
            <td>密碼</td>
            <td>
                <input type="password" name="password" placeholder="請輸入密碼"/>
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="登錄"/>
                <input type="reset" value="重置"/>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

10.首頁 index.HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

<head>
    <title>首頁</title>
</head>
<body>
<h1>首頁</h1>
<hr>
<ul>
    <li><a href="user/index" >個人中心</a></li>
    <li><a href="vip/index">會員中心</a></li>
    <p th:text="'用戶編號:' + ${uid}"/>
    <p th:text="'用戶姓名:' + ${uname}"/>
    <shiro:hasPermission name="user1"><h2>admin</h2></shiro:hasPermission>
    <shiro:hasPermission name="vip1"><h2>vip</h2></shiro:hasPermission>
    <shiro:hasPermission name="svip1"><h2>admin</h2></shiro:hasPermission>
    <li><a href="logout">退出登錄</a></li>
</ul>
</body>
</html>

11.數據庫結構

user 

 

role

 

user_role

 

permission

 

 role_permission

 12.  頁面測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章