Spring Boot 整合Shiro 最新 最全面(Mybatis版本)


1: 依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>${thymeleaf-shiro.version}</version>
        </dependency>
        <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.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-spring</artifactId>
           <version>1.5.1</version>
      </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
        </dependency>
    </dependencies>

2: application.xml配置

#server.servlet.context-path=/hello
##端口號
server.port=8888

##檢查 mybatis 配置是否存在,一般命名為 mybatis-config.xml
mybatis.check-config-location =true
##配置文件位置 Resource下mybaits文件夾
mybatis.config-location=classpath:mybatis/mybatis-config.xml
## mapper xml 文件地址  Resource下mapper文件夾
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
##日志級別 com.yang.dao 為包名
#logging.level.com.dgw.springbootandshiro.dao=debug
##數據庫url
spring.datasource.url=jdbc:mysql://localhost/rbac?userSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
##數據庫用戶名
spring.datasource.username=root
##數據庫密碼
spring.datasource.password=root
##數據庫驅動
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


spring.thymeleaf.check-template-location=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# 建議在開發時關閉緩存,不然沒法看到實時頁面
spring.thymeleaf.cache=false
##去除thymeleaf的html嚴格校驗
spring.thymeleaf.mode=HTML

spring.main.allow-bean-definition-overriding=true

#debug=true
#簡單設置一下日志等級
logging.level.web=info
logging.level.root=info

3: Mybatis 配置:

  目錄結構如下:

 image

Mybatis 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="Integer" type="java.lang.Integer" />
        <typeAlias alias="Long" type="java.lang.Long" />
        <typeAlias alias="HashMap" type="java.util.HashMap" />
        <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
        <typeAlias alias="ArrayList" type="java.util.ArrayList" />
        <typeAlias alias="LinkedList" type="java.util.LinkedList" />
        <typeAlias alias="user" type="com.dgw.springbootandshiro.bean.User"/>
    </typeAliases>
</configuration>
 

實體類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String salt;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    private Integer id;
    private String roleName;
    private Date createTime;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
    private Integer id;
    private String permissionName;
    private Date createTime;
}


dao接口: 部分 其余扎着框框畫鴨蛋

@Mapper
public interface UserMapper {
    User queryUserByUsername(@Param("username") String username);
    Integer insertUser(User user);
}

services實現:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userDAO;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = SQLException.class)
    public User queryUserByUsername(String username) {
        return userDAO.queryUserByUsername(username);
    }

    @Override
    public Integer insertUser(User user) {
        // 加密
        String salt = UUID.randomUUID().toString();
        String s = new Sha256Hash(user.getPassword(), salt, MyConstant.INTERCOUNT).toBase64();
        // 設置密文
        user.setPassword(s);
        // 設置鹽
        user.setSalt(salt);
        return userDAO.insertUser(user);
    }
}


3: Shiro配置

Realm配置i

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;
    // 授權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String  username  = (String)principalCollection.getPrimaryPrincipal();
        // 查詢當前用戶的權限信息
        Set<String> roles = roleService.queryAllRolenameByUsername(username);
        Set<String> perms = permissionService.queryAllPermissionByUsername(username);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        simpleAuthorizationInfo.setStringPermissions(perms);

        return simpleAuthorizationInfo;
    }

    //認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String usernmae = (String) authenticationToken.getPrincipal();
        User user = userService.queryUserByUsername(usernmae);
        if (user == null) {
            return null;
        }
        //這里會去校驗密碼是否正確
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                getName()
        );
        return authenticationInfo;
    }
}
 

Shiroconfig 配置


@Configuration
public class ShiroConfig {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        logger.info("啟動shiroFilter--時間是:" + new Date());
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //shiro攔截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
        //<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->
        Map<String, Filter> filterMap=new LinkedHashMap<String, Filter>();
        filterMap.put("MyRememberFilter", new MyRememberFilter());

        shiroFilterFactoryBean.setFilters(filterMap);

        // 如果不設置默認會自動尋找Web工程根目錄下的"/login"頁面,即本文使用的login.html
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/main");
        //錯誤頁面,認證不通過跳轉
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        //未授權界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");


        // 配置不被攔截的資源及鏈接
        filterChainDefinitionMap.put("/static/**", "anon");
        // 退出過濾器
        filterChainDefinitionMap.put("/logout", "logout");
        //開啟注冊頁面不需要權限
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/register", "anon");

        //配置需要認證權限的
        filterChainDefinitionMap.put("/**", "authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 配置shiro的生命周期
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 定義加密規則 存入密碼時也必須加密
     */
    @Bean
    public HashedCredentialsMatcher myMatcher(){
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256");
        // true means hex encoded, false means base64 encoded
        matcher.setStoredCredentialsHexEncoded(false);
        matcher.setHashIterations(10000);
        return  matcher;
    }

    public class MyRememberFilter extends FormAuthenticationFilter {
        protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, Object mappedValue){
            Subject subject=getSubject(request,response);
            if(!subject.isAuthenticated() && subject.isRemembered()){
                if(subject.getSession().getAttribute("user")==null &&subject.getPrincipal()!=null){
                    subject.getSession().setAttribute("user",subject.getPrincipal());
                }

            }
            return subject.isAuthenticated() || subject.isRemembered();
        }
    }
   /* private class MyMatcher extends HashedCredentialsMatcher {
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            String pwd = encrypt(String.valueOf(usernamePasswordToken.getPassword()));
            String mysqlpwd = (String) info.getCredentials();
            logger.info("密碼"+mysqlpwd);
            logger.info("密碼"+pwd);
            return this.equals(pwd, mysqlpwd);
        }

        //將傳進來的密碼進行加密的方法
        private String encrypt(String data) {
            // 加密
            String salt = UUID.randomUUID().toString();
            String s = new Sha256Hash(data, salt, MyConstant.INTERCOUNT).toBase64();
            return s;
        }
    }*/

    /**
     * 自定義身份認證Realm(包含用戶名密碼校驗,權限校驗等)
     */
    @Bean
    public ShiroRealm myShiroRealm() {
        ShiroRealm myShiroRealm = new ShiroRealm();
        myShiroRealm.setCredentialsMatcher(myMatcher());
        return myShiroRealm;
    }

    /**
     * 使用shiro 支持thymeleaf 模版引擎
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 配置記住我Cookie對象參數,rememberMeCookie()方法是設置Cookie的生成模版
     */
    public SimpleCookie rememberMeCookie() {
        //這個參數是cookie的名稱,對應前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //cookie生效時間為10秒
        simpleCookie.setMaxAge(100000);
        return simpleCookie;
    }

    /**
     * 配置Cookie管理對象,rememberMeManager()方法是生成rememberMe管理器
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * 配置sessionCookie對象參數,sessionCookie()方法是設置Cookie的生成模版
     */
    public SimpleCookie sessionIdCookie() {
        //這個參數是cookie的名稱,對應前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("JSESSIONID");
        //只允許http請求訪問cookie
        simpleCookie.setHttpOnly(true);
        //cookie生效時間為10秒 默認為-1
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 配置Cookie管理對象,rememberMeManager()方法是生成rememberMe管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
        webSessionManager.setSessionIdCookie(sessionIdCookie());
        // session全局超時時間, 單位:毫秒 ,30分鍾 默認值為1800000
        webSessionManager.setGlobalSessionTimeout(1800000);
        //開啟檢測器,默認開啟
        webSessionManager.setSessionIdUrlRewritingEnabled(true);
        // 檢測間隔事件 時間為1小時
        webSessionManager.setSessionValidationInterval(3600000);
        // 設置監聽器
        //webSessionManager.setSessionListeners();
        return webSessionManager;
    }

    @Bean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }

    @Bean(name = "securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入自定義myRealm
        securityManager.setRealm(myShiroRealm());
        //注入自定義cacheManager
        securityManager.setCacheManager(cacheManager());
        //注入記住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        //注入自定義sessionManager
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }


    //開啟shiro aop注解支持,不開啟的話權限驗證就會失效
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        sourceAdvisor.setSecurityManager(securityManager);
        return sourceAdvisor;
    }

    //配置異常處理,不配置的話沒有權限后台報錯,前台不會跳轉到403頁面
    @Bean(name = "simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//數據庫異常處理
        mappings.setProperty("UnauthorizedException", "403");
        simpleMappingExceptionResolver.setExceptionMappings(mappings);  // None by default
        simpleMappingExceptionResolver.setDefaultErrorView("403");    // No default
        simpleMappingExceptionResolver.setExceptionAttribute("ex");     // Default is "exception"
        return simpleMappingExceptionResolver;
    }
}


4: thymeleaf

login.html

<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
      xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Insert title here</title>
</head>
<body>
<form method="post" action="login" >
    <label for="name">用戶名</label>
    <shiro:authenticated>
        <input id="name" type="text" name="username" value="<shiro:principal/>" >
    </shiro:authenticated>
    <shiro:notAuthenticated>
        <input id="name" type="text" name="username" value=" " >
    </shiro:notAuthenticated>
    <br>
    <label for="pass">密碼</label>
    <input id="pass" type="text" name="password">
    <button type="submit">提交</button>

</form>
</body>
</html>

5 controller

@Controller
@RequestMapping("/user")
public class LoginController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private UserService userService;

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @PostMapping("/login")
    public String loginLogic(User user) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        // 登錄失敗會拋出異常,則交由異常解析器處理
        token.setRememberMe(true);
        subject.login(token);

        return "main";
    }

    @GetMapping("/register")
    public String regiter() {
        return "register";
    }


    @PostMapping("/register")
    public String logicRegiter(User user) {
        userService.insertUser(user);
        return "redirect:login";
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM