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 配置:
目錄結構如下:
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"; } }

