springboot整合shiro 完成用戶登錄接口

shiro是輕量級框架,比SpringSecurity簡單,下面是我springboot整合shiro的經驗。

(因為今天沒有時間調試前後端分離的shiro配置,這裡僅僅是shiro和springboot的簡單整合)

shiro功能

  1. Subject:主體,一般指用戶。
  2. SecurityManager:安全管理器,管理所有Subject,可以配合內部安全組件。
  3. Realms:用於進行權限信息的驗證。

項目整合Shiro

<code>

  org.apache.shiro
  shiro-spring
  1.4.0
/<code>

項目結構圖


springboot整合shiro 完成用戶登錄接口


UserInfo(用戶類)

<code>@Data
public class UserInfo {
    private int userId;
    private String  username;
    private String password;
    private Set roles;
}/<code>

Role(角色類)

<code>@Data
public class Role {
    private Long id;

    private String description;

    private String name;

    private Integer status;
    /**
     * 角色對應權限集合
     */
    private Set permissions;
    public Long getId() {
        return id;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description == null ? null : description.trim();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }
}/<code>

Permission.java(權限類):

<code>public class Permission {
    private Long id;

    private String description;

    private String name;

    private Long parentId;

    private Integer version;

    private Integer weight;

    public Long getId() {
        return id;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description == null ? null : description.trim();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Long getParentId() {
        return parentId;
    }

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

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }
}/<code>

LoginServiceImpl(登錄服務類--查詢用戶)

<code>
@Service
public class LoginServiceImpl implements LoginService {

    @Override
    public UserInfo getUser(String getMapByName) {
        // 查詢用戶信息, 此處就是shiro每次查詢用戶的接口
        return getMapByName(getMapByName);
    }

    /**
     * 模擬數據庫查詢
     * @param userName
     * @return
     */
    private UserInfo getMapByName(String userName){
        //共添加兩個用戶,兩個用戶都是admin一個角色,
        //wsl有query和add權限,zhangsan只有一個query權限
        Permission permissions1 = new Permission();
        permissions1.setId(1L);
        permissions1.setDescription("user:list");
        Set permissionsSet = new HashSet<>();
        permissionsSet.add(permissions1);
        Role role = new Role();
        role.setId(1L);
        role.setName("admin");
        role.setPermissions(permissionsSet);
        Set roleSet = new HashSet<>();
        roleSet.add(role);
        UserInfo user = new UserInfo();
        user.setUserId(1);
        user.setUsername("admin");  // 賬號
        user.setPassword("admin"); // 密碼  目前還是明文保存
        user.setRoles(roleSet);
        Map map = new HashMap<>();
        map.put(user.getUsername(), user);

        return map.get(userName);
    }
}
/<code>

自定義Realm用於查詢用戶的角色和權限信息並保存到權限管理器:

UserRealm.java

<code>import com.whf.blog.bean.UserInfo;
import com.whf.blog.pojo.Permission;
import com.whf.blog.service.LoginService;
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.springframework.beans.factory.annotation.Autowired;


public class UserRealm  extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;
		// 授權查詢回調函數, 進行鑑權但緩存中無用戶的授權信息時調用.SecurityUtils.getSubject().isPermitted 執行
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //獲取用戶名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //查詢用戶信息
        UserInfo user = loginService.getUser(name);
        //添加角色和權限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //添加角色
        //添加權限
        user.getRoles().forEach(role -> {
            simpleAuthorizationInfo.addRole(role.getName());
            for (Permission permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getName());
            }
        });
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //  subject.login(usernamePasswordToken);  此時方法會走到這裡,如果用戶發現賬戶密碼又問題可以斷點此處查看原因
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        //獲取用戶信息
        String name = authenticationToken.getPrincipal().toString();
        UserInfo user = loginService.getUser(name);
        if (user == null) {
            //這裡返回後會報出對應異常
            return null;
        } else {
            //這裡驗證authenticationToken和simpleAuthenticationInfo的信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
            return simpleAuthenticationInfo;
        }
    }
}/<code> 

ShiroConfig(配置類)

<code>
import org.apache.shiro.mgt.SecurityManager;
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;

@Configuration
public class shiroConfig {
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    //自己的驗證方式
    @Bean
    public UserRealm myShiroRealm() {
        UserRealm customRealm = new UserRealm();
        return customRealm;
    }

    //權限管理,
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工廠,設置對應的過濾條件和跳轉條件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map map = new HashMap<>();
        //登出
        map.put("/logout", "logout");
        //對所有用戶認證
        map.put("/**", "authc");
//        //登錄  登錄跳過驗證
        shiroFilterFactoryBean.setLoginUrl("/login");
//        //首頁
//        shiroFilterFactoryBean.setSuccessUrl("/index");
//        //錯誤頁面,認證不通過跳轉
//        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
/<code>

LoginController.(外部訪問)

<code> @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public Result login(@RequestBody UserInfo user) {
        Result result = new Result();
        //添加用戶認證信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUsername(),
                user.getPassword()
        );
        try {
            //進行驗證,這裡可以捕獲異常,然後返回對應信息
            subject.login(usernamePasswordToken);
            result.setCode(200);
            result.setUserId("1");
//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            result.setCode(400);
        } catch (AuthorizationException e) {
            e.printStackTrace();
            result.setCode(400);
        }
        return result;
    }/<code>
註解驗證角色和權限的話無法捕捉異常,添加一個異常攔截器

MyExceptionHandler

<code>

import com.whf.blog.resp.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@Slf4j
public class MyExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    public Result ErrorHandler(AuthorizationException e) {
        log.error("權限驗證失敗!", e);
        Result result = new Result();
        result.setCode(400);
        result.setMessage( "權限驗證失敗!");
        return result;
    }
}
/<code>


分享到:


相關文章: