SpringBoot 통합 Shiro (초 상세 - 인증, 동적 권한 부여, remembeMe)

24175 단어 shiro 권한SpringBoot
SpringBoot 통합 Shiro (초 상세)
이 프로젝트 는 SpringBoot 를 통 해 shiro 프레임 워 크 를 통합 하여 MybatisPlus 코드 생 성 기 를 사용 하여 간단 한 코드 를 만 들 었 습 니 다. 프로젝트 는 shiro 동적 권한 수여 와 인증, remembeMe 기억 기능 을 포함 합 니 다.
프로젝트 준비
(1) 가 져 오기 의존


	4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.1.6.RELEASE
		
	
	com.example
	springboot-shiro
	0.0.1-SNAPSHOT
	springboot-shiro
	Demo project for Spring Boot Shiro

	
		1.8
	

	
		
			org.springframework.boot
			spring-boot-starter-thymeleaf
		
		
			org.springframework.boot
			spring-boot-starter-web
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		

		
			mysql
			mysql-connector-java
			5.1.46
		

		
			com.baomidou
			mybatis-plus-boot-starter
			3.1.0
		

		
		
			com.baomidou
			mybatis-plus-generator
			3.1.0
		
		
			org.apache.velocity
			velocity-engine-core
			2.1
		

		
			junit
			junit
			4.12
		

		
		
			org.apache.shiro
			shiro-spring
			1.4.0
		
       
		
			org.springframework.boot
			spring-boot-devtools
			true
		
		
		
			net.sf.ehcache
			ehcache-core
			2.4.8
		
		
			org.apache.shiro
			shiro-ehcache
			1.4.0
		
		
		
			com.alibaba
			fastjson
			1.2.13
		
		
			org.springframework
			spring-context-support
			5.1.8.RELEASE
		


	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
				
				
					true
				
			
		
	




(2) SpringBoot 프로필 설정
server.port=80

spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/

#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/crm?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#mybatis-plus
mybatis-plus.type-aliases-package=com.example.leilei.entity

#   

#       
spring.devtools.restart.enabled=true
#       ,      
spring.freemarker.cache=false

(3) SpringBoot 시작 클래스 설정
@SpringBootApplication
@MapperScan(basePackages = "com.example.leilei.mapper")
public class SpringbootShiroApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootShiroApplication.class, args);
	}

}

(4) Mybatis - plus 의 코드 생 성기 사용
도 구 를 합 리 적 으로 사용 하면 개발 시간 을 줄 일 수 있다.
public class CodeGenerator {

    public static void main(String[] args) throws InterruptedException {
        //    Mybatis-Plus.properties       
        ResourceBundle rb = ResourceBundle.getBundle("springboot-shiro");
        AutoGenerator mpg = new AutoGenerator();
        //     
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(rb.getString("OutputDir"));
        gc.setFileOverride(true);
        gc.setActiveRecord(true);//    activeRecord   
        gc.setEnableCache(false);// XML     
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(false);// XML columList
        gc.setAuthor(rb.getString("author"));
        mpg.setGlobalConfig(gc);
        //      
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setTypeConvert(new MySqlTypeConvert());
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername(rb.getString("jdbc.user"));
        dsc.setPassword(rb.getString("jdbc.pwd"));
        dsc.setUrl(rb.getString("jdbc.url"));
        mpg.setDataSource(dsc);
        //     
        StrategyConfig strategy = new StrategyConfig();
       // strategy.setTablePrefix(new String[] { "t_" });//             
        strategy.setNaming(NamingStrategy.underline_to_camel);//       
        strategy.setInclude(new String[]{"real_eseate"}); //       
        mpg.setStrategy(strategy);
        //    
        PackageConfig pc = new PackageConfig();
        pc.setParent(rb.getString("parent"));
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setEntity("domain");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        //        ,    VM     cfg.abc 【  】
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
            }
        };

        List focList = new ArrayList();

        //    xml       
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return rb.getString("OutputDirXml")+ "/cn/leilei/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
            }
        });

        //    query     
/*        focList.add(new FileOutConfig("/templates/query.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return rb.getString("OutputDomainDir")+ "/cn/leilei/query/" + tableInfo.getEntityName() + "Query.java";
            }
        });*/

        //    domain     
        focList.add(new FileOutConfig("/templates/entity.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return rb.getString("OutputDomainDir")+ "/cn/leilei/domain/" + tableInfo.getEntityName() + ".java";
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        //        ,   copy    mybatis-plus/src/main/resources/templates       ,
        //         src/main/resources/templates    ,            ,          
        TemplateConfig tc = new TemplateConfig();
        //tc.setController("/templates/controller.java.vm");
        //                OR Null        。
        tc.setEntity(null);
        tc.setXml(null);
        mpg.setTemplate(tc);

        //     
        mpg.execute();
    }


}


springboot-shiro.perproties
#    
OutputDir=E://EEworkspac//springbootshiro//springboot-shiro//src//main//java
#query domain     
OutputDomainDir=E://EEworkspac//springbootshiro//springboot-shiro//src//main//java


#  
author=lei
#   
jdbc.user=root
jdbc.pwd=root
jdbc.url=jdbc:mysql:///crm?useUnicode=true&characterEncoding=utf8

#   
parent=cn.leilei
#xml    resources  
OutputDirXml=E://EEworkspac//springbootshiro//springboot-shiro//src//main//resources

2. 백 엔 드 코드 작성
(1) shiro 의 생명주기 설정
이 shiro 라 이 프 사이클 의 Bean 을 따로 설정 해 주세요.
/**
 *   shiro     Bean       
 */
@Configuration
public class ShiroLifecycleBeanPostProcessorConfig {

    /**
     * Shiro       
     *
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}

(2) Realm 작성 인증, 권한 부여
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;
    @Autowired
    private IPermissionService permissionService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
        Long empId = employee.getId();
        //    id      
        List permissions = permissionService.getByEmpId(empId);
		//         shiro
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for (Permission permission : permissions) {
            info.addStringPermission(permission.getSn());
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        //         
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Employee employee = employeeService.getOne(wrapper);
        if(employee==null){
            throw new UnknownAccountException(username);
        }
        //  info  
        return new SimpleAuthenticationInfo(employee,employee.getPassword(), ByteSource.Util.bytes(MD5Utils.SALT),getName());
    }
}

권한 수여 방법 은 링크 조회 가 필요 합 니 다.
elmplyee 에서 permission 표 조회
간단하게 썼 지만 실제 개발 에 서 는 sql 문 구 를 사용 하지 마 세 요 *
    


(3) shiro 의 설정 클래스
현재 프로젝트 에는 인증, 권한 수여, remembeMe 가 포함 되 어 있 습 니 다.
package com.example.leilei.config;

import com.example.leilei.entity.Permission;
import com.example.leilei.service.IEmployeeService;
import com.example.leilei.service.IPermissionService;
import com.example.leilei.shiro.realm.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
@AutoConfigureAfter(ShiroLifecycleBeanPostProcessorConfig.class)//  Bean       
public class ShiroConfig {

    @Autowired
    private IPermissionService permissionService;

    /**
     * SecurityManager    Bean
     * @return
     */
    @Bean(name = "securityManager")
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //  Realm
        securityManager.setRealm(myRealm());
        //        
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     *      -       
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//    
        hashedCredentialsMatcher.setHashIterations(10);//    
        return hashedCredentialsMatcher;
    }
    /**
     * cookie  ;
     * rememberMeCookie()     Cookie     ,  cookie name,cookie       。
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //     cookie   ,     checkbox name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie    ;
     * rememberMeManager()     rememberMe   ,      rememberMe      securityManager 
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie                   AES       (128 256 512  )
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }

    /**
     *     Realm
     * @return
     */
    @Bean
    public MyRealm myRealm(){
        MyRealm myRealm = new MyRealm();
        //        ,             
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myRealm;
    }

    /**
     *            
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //     
        Map filterChainDefinitionMap = new LinkedHashMap();
        filterChainDefinitionMap.put("/static/**", "anon");   //anon        
        filterChainDefinitionMap.put("/logout", "logout");    //shiro     ,        

        //    ,      
        List permissions = permissionService.list();
        for (Permission permission : permissions) {
            filterChainDefinitionMap.put(permission.getUrl(),"perms["+permission.getSn()+"]");
        }

        filterChainDefinitionMap.put("/**", "authc");

        //              ,      ,        
        shiroFilterFactoryBean.setLoginUrl("/login");
        //            
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //                 ,      ,        
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
}


(4) 도구 클래스 작성
(1)Ajax
/**
 *        ajax  ,       ,          
 *
 */
public class AjaxResult {
    private Boolean success = true;
    private String msg;

    private AjaxResult() {
    }

    /**
     *   ajax
     * @return          
     */
    public static AjaxResult success(){
        return new AjaxResult();
    }

    /**
     *    ajax
     * @param msg     
     * @return         
     */
    public static AjaxResult error(String msg){
        AjaxResult ajaxResult = success();;
        ajaxResult.setSuccess(false);
        ajaxResult.setMsg(msg);
        return ajaxResult;
    }

    public Boolean getSuccess() {
        return success;
    }

    private void setSuccess(Boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    private void setMsg(String msg) {
        this.msg = msg;
    }
}

(2) 세 션 필드 대상 (로그 인 사용자) 액세스 값
public class SessionUtil {
    public static final String LOGINSESSION = "loginuser";

    //            
    public static void setSession(Employee employee){//          
        Subject subject = SecurityUtils.getSubject();//      
        subject.getSession().setAttribute(LOGINSESSION,employee);
    }

    //Session         
    public static Employee getSession(){//           
        Subject subject = SecurityUtils.getSubject();//      
        return (Employee) subject.getSession().getAttribute(LOGINSESSION);
    }
}

(3) MD5 암호 화
public class MD5Utils {

    public static final String SALT = "fm";
    public static final int ITERATIONS = 10;

    /**
     *   
     * @param source
     * @return
     */
    public static String encrype(String source){
        SimpleHash simpleHash = new SimpleHash("MD5",source,SALT,ITERATIONS);
        return simpleHash.toString();
    }

    public static void main(String[] args) {

        System.out.println(encrype("admin"));

    }

}

(5) 컨트롤 러 층 로그 인 로그아웃 방법
@Controller
public class LoginController {

    /**
     *        
     * @return
     */
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String login(){
    
        Subject subject = SecurityUtils.getSubject();
        //           rememberMe 
        if (subject.isRemembered()){
            return "main";
        //           
        }else if(subject.isAuthenticated()){
            return "main";
        }
        return "login";
    }

    /**
     *     
     * @param employee
     * @return
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public AjaxResult login(@RequestBody Employee employee){
        //          
        Subject subject = SecurityUtils.getSubject();
        /*Boolean rememberMe = true;*/
        //             tocken  
        UsernamePasswordToken token = new UsernamePasswordToken(employee.getUsername(),employee.getPassword(),employee.getRememberMe());
        try {
            //     tocken      shiro    ---->      MyRealm  AuthenticationInfo  ,    ,
            if(!subject.isAuthenticated()){//                  
                subject.login(token);//    
            }
            //               
            Employee logiuser = ((Employee) subject.getPrincipal());//      
            SessionUtil.setSession(logiuser);
            return AjaxResult.success();
        }  catch (UnknownAccountException e) {
            e.printStackTrace();
            return AjaxResult.error("     ");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            return AjaxResult.error("    ");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return AjaxResult.error("    ,    ");
        }
    }

    //    
    @RequestMapping(value = "/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        return "redirect:/logout";
    }


}

3. VUE / ElementUI 전단 기반 작성
전단 에 사용 되 는 정적 자원 을 resource / statc 아래 에 놓 습 니 다.
전단 에 사용 할 페이지 는 resource / templates 아래 에 놓 습 니 다.
(1) 비 계 를 설치 하거나 Elementui 를 간단하게 가 져 오 는 js css
(2) 로그 인 인터페이스



    
    
    Title
    
    
    
    
    



new Vue({ el: "#app", data: { logining: false, loginUser: { username: '', password: '', rememberMe:false }, loginFormRules: { username: [ {required: true, message: ' ', trigger: 'blur'}, ], password: [ {required: true, message: ' ', trigger: 'blur'}, ] }, checked: false, }, methods: { handleLogin(ev) { var _this = this; this.$refs.loginForm.validate((valid) => { if (valid) { this.logining = true; // this.loginUser.rememberMe=this.checked axios.post("login", this.loginUser) .then((res) => { let data = res.data; if (data.success) { // this.logining = false; // location.href = "main"; } else { this.logining = false; this.$message.error(data.msg, "error"); } }) } else { this.$message.error(' '); return false; } }); } } });

다른 페이지 는 작성 하면 됩 니 다. 최종 적 으로 제 소스 코드 를 사용 하면 동적 권한 수여 와 인증, remembeMe 기능 이 실 현 됩 니 다.

좋은 웹페이지 즐겨찾기