1. 程式人生 > 其它 >springboot 接入shiro

springboot 接入shiro

1、建表、建實體類、service、mapper等,目的是查詢使用者資訊和角色資訊

檢視程式碼
-- 三張表,二張關係表
create table if not exists sys_permissions
(
	id int auto_increment comment '編號'
		primary key,
	permission varchar(100) null comment '許可權編號',
	description varchar(100) null comment '許可權描述',
	rid int null comment '此許可權關聯角色的id',
	available int default 0 null comment '是否鎖定',
	constraint idx_sys_permissions_permission
		unique (permission)
);

create table if not exists sys_roles
(
	id int auto_increment comment '角色編號'
		primary key,
	role varchar(100) null comment '角色名稱',
	description varchar(100) null comment '角色描述',
	pid int null comment '父節點',
	available int default 0 null comment '是否鎖定',
	constraint idx_sys_roles_role
		unique (role)
);

create table if not exists sys_roles_permissions
(
	role_id int null comment '角色編號',
	permission_id int null comment '許可權編號'
);

create table if not exists sys_users
(
	id int auto_increment comment '編號'
		primary key,
	username varchar(100) null comment '使用者名稱',
	password varchar(100) null comment '密碼',
	salt varchar(100) null comment '鹽值',
	role_id varchar(50) null comment '角色列表',
	locked int default 0 null comment '是否鎖定',
	constraint idx_sys_users_username
		unique (username)
);

create table if not exists sys_users_roles
(
	user_id int null comment '使用者編號',
	role_id int null comment '角色編號'
);

2、pom

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

3、CustomRealm:自定義Realm用於查詢使用者的角色和許可權資訊並儲存到許可權管理器

import com.pingan.domain.login.Permissions;
import com.pingan.domain.login.Role;
import com.pingan.domain.login.User;
import com.pingan.servcie.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;
import org.springframework.util.ObjectUtils;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //獲取登入使用者名稱
        String name = (String) principalCollection.getPrimaryPrincipal();
        //查詢使用者名稱稱
        User user = loginService.getUserByName(name);
        //新增角色和許可權
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        //新增角色
        for (Role role : user.getRoleSet()) {
            simpleAuthorizationInfo.addRole(role.getRoleName());

            //新增許可權
            for (Permissions permissions : role.getPermissionSet()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
            }
        }

        return simpleAuthorizationInfo;
    }
    //認證  返回null 或者SimpleAuthenticationInfo拋異常都算認證失敗
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (ObjectUtils.isEmpty(authenticationToken.getPrincipal())){
            return null;
        }

        String name = authenticationToken.getPrincipal().toString();
        User user = loginService.getUserByName(name);
        if (user == null) {
            return null;
        } else {
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
            return simpleAuthenticationInfo;
        }
    }
}

4、ShiroConfig:shiro的配置類

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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
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.LinkedHashMap;

@Configuration
public class ShiroConfig {

    // 授權所用配置
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    //將自定義的驗證方式加入容器
    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

    // 設定session過期時間
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 設定session過期時間10s
        sessionManager.setGlobalSessionTimeout(10*1000L);
        return sessionManager;
    }

    //許可權管理,配置主要是Realm的管理認證
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(customRealm());
        defaultWebSecurityManager.setSessionManager(sessionManager());
        return defaultWebSecurityManager;
    }


    //Filter工廠,設定對應的過濾條件和跳轉條件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // 不攔截的路徑
        // swagger
        map.put("/swagger-ui.html", "anon");
        map.put("/swagger/**", "anon");
        map.put("/swagger-resources/**", "anon");
        map.put("/v2/**", "anon");
        map.put("/webjars/**", "anon");
        map.put("/configuration/**", "anon");
        map.put("/doc.html","anon");

        // 其它 anon 代表不攔截
        map.put("/parent/*", "anon");

        //登出 並清理seesion?登出後需要重新登陸
        map.put("/logout", "logout");
        // 攔截所有路徑(除了不攔截的)
        map.put("/**", "authc");
        shiroFilterFactoryBean.setLoginUrl("/testLogin");
        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;
    }
    
    // 自定義密碼加密解密方式
    // 在 myRealm重寫這個方法設定新較驗類 CustomCredentialsMatcher
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        CustomCredentialsMatcher customCredentialsMatcher = new CustomCredentialsMatcher();
        super.setCredentialsMatcher(customCredentialsMatcher);
    }
}

密碼較驗類

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.util.StringUtils;

// 自定義密碼較驗方式 寫完這個類後要在自定義類MyRealm裡重寫 setCredentialsMatcher
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher  {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        String password = String.valueOf(usernamePasswordToken.getPassword());
        String host = usernamePasswordToken.getHost();
        System.out.println("host = " + host);

        if (username.equals("root")) {
            System.out.println("dddd = ----------" );
            return true;
        }
        System.out.println(username+"============");
        System.out.println("password = " + password);
        return false;
//        return super.doCredentialsMatch(token, info);
    }
}

5、登陸Controller

@RestController
public class LoginController {

    @Autowired
    private UserMapper userMapper;

   @GetMapping("/login")
    private Object login(@RequestParam("username") String username, @RequestParam("password")String password, HttpServletRequest request) {
        String ipAddress = getIpAddress(request);
    
       // 呼叫shiro 傳入username,password,ip (ip可傳可不傳)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password,ipAddress);
        try {
            subject.login(usernamePasswordToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "登陸失敗";
        }
    
        return "登陸成功啦啦啦啦";
}

    //通過request取到ip地址
    private static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

6、在對應用controller方法 或者service方法上加註解

@RequiresRoles

例如:@RequiresRoles(“aRoleName”);

void someMethod();

如果subject中有aRoleName角色才可以訪問方法someMethod。如果沒有這個許可權則會丟擲異常AuthorizationException。

@RequiresPermissions

例如: @RequiresPermissions({“file:read”, “write:aFile.txt”} )
void someMethod();

要求subject中必須同時含有file:read和write:aFile.txt的許可權才能執行方法someMethod()。否則丟擲異常AuthorizationException。