ç¹å»ä¸æ¹â Javaèµæç«Â âï¼éæ©âæ æå ¬ä¼å·â
ä¼è´¨æç« ï¼ç¬¬ä¸æ¶é´éè¾¾
   åå®ç¼çæ¶æ森æ   |  ä½è
urlify.cn/UnYBbm  |  æ¥æº
ä¸ï¼å¨ææé管ççä¼ç¹å缺ç¹
1,ä¼ç¹:
 å 为æ§å¶æéçæ°æ®ä¿åå¨äºmysqlæå ¶ä»åå¨ç³»ç»ä¸ï¼
 å¯ä»¥å¨æä¿®æ¹æéæ§å¶ï¼æ éæ¹å¨ä»£ç åéå¯åºç¨,
 æéåæ´æ¶çµæ´»æ¹ä¾¿
2,缺ç¹:
  æéç设置éè¦ä¿åå¨å¤é¨åå¨ç³»ç»ï¼
  æ¯æ¬¡requestæ¶é½éè¦æ¥åºå¤çï¼
  é«å¹¶åæ¶å½±åæç
说æï¼åå®ç¼çæ¶æ森ææ¯ä¸ä¸ªä¸æ³¨æ¶æçå客ï¼å°åï¼https://www.cnblogs.com/architectforest
     对åºçæºç å¯ä»¥è®¿é®è¿éè·åï¼Â https://github.com/liuhongdi/
说æï¼ä½è :åå®ç¼ é®ç®±: [email protected]
äºï¼æ¼ç¤ºé¡¹ç®çç¸å ³ä¿¡æ¯
1,项ç®å°å:
https://github.com/liuhongdi/securitydynamicÂ
2,项ç®åè½è¯´æ
     éè¿ä¿®æ¹mysqlæ°æ®åºä¸çæ°æ®ï¼
     å®ç°å¯¹æééªè¯çå¨ææ§å¶ï¼æ éä¿®æ¹ä»£ç åéå¯åºç¨
3,项ç®ç»æï¼å¦å¾:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SO3QTZ5MTYidzYhJ2Y2EzYyIzN3Q2NyMGM4gTN4Q2Yw8CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
ä¸ï¼é ç½®æ件说æ
1,pom.xml
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-securityartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-thymeleafartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-validationartifactId> dependency> <dependency> <groupId>org.mybatis.spring.bootgroupId> <artifactId>mybatis-spring-boot-starterartifactId> <version>2.1.3version> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <scope>runtimescope> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>fastjsonartifactId> <version>1.2.72version> dependency>
2ï¼application.properties:
#thymeleafspring.thymeleaf.cache=falsespring.thymeleaf.encoding=UTF-8spring.thymeleaf.mode=HTMLspring.thymeleaf.prefix=classpath:/templates/spring.thymeleaf.suffix=.html#mysqlspring.datasource.url=jdbc:mysql://localhost:3306/security?characterEncoding=utf8&useSSL=falsespring.datasource.username=rootspring.datasource.password=lhddemospring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#mybatismybatis.mapper-locations=classpath:/mapper/*Mapper.xmlmybatis.type-aliases-package=com.example.demo.mapper#errorserver.error.include-stacktrace=always#loglogging.level.org.springframework.web=tracelogging.level.org.springframework.security=debug
3,æ°æ®åº:
 建ç«å个表çsql:
CREATE TABLE `sys_user` ( `userId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `userName` varchar(100) NOT NULL DEFAULT '' COMMENT 'ç¨æ·å', `password` varchar(100) NOT NULL DEFAULT '' COMMENT 'å¯ç ', `nickName` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'æµç§°', PRIMARY KEY (`userId`), UNIQUE KEY `userName` (`userName`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='ç¨æ·è¡¨'
INSERT INTO `sys_user` (`userId`, `userName`, `password`, `nickName`) VALUES(1, 'lhd', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', 'èå'),(2, 'admin', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '管çå'),(3, 'merchant', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', 'åæ·èå¼ ');
CREATE TABLE `sys_user_role` ( `urId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `userId` int(11) NOT NULL DEFAULT '0' COMMENT 'ç¨æ·id', `roleName` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'è§è²id', PRIMARY KEY (`urId`), UNIQUE KEY `userId` (`userId`,`roleName`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='ç¨æ·è§è²å
³è表'
INSERT INTO `sys_user_role` (`urId`, `userId`, `roleName`) VALUES(1, 2, 'ADMIN'),(2, 3, 'MERCHANT');
CREATE TABLE `sys_menu` ( `menuId` int(11) NOT NULL AUTO_INCREMENT, `pattern` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, PRIMARY KEY (`menuId`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='method'
INSERT INTO `sys_menu` (`menuId`, `pattern`) VALUES(1, '/home/**'),(2, '/login/**'),(3, '/js/**'),(4, '/admin/**'),(5, '/merchant/**');
CREATE TABLE `sys_menu_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `menuId` int(11) DEFAULT NULL, `roleName` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT 'roleåå', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='menuè§è²å¯¹åºè¡¨'
INSERT INTO `sys_menu_role` (`id`, `menuId`, `roleName`) VALUES(1, 1, 'ALL'),(2, 2, 'ALL'),(3, 3, 'ALL'),(4, 4, 'ADMIN'),(5, 5, 'MERCHANT'),(6, 5, 'ADMIN');
 说æï¼sys_user表ä¸ï¼3个ç¨æ·çå¯ç é½æ¯111111ï¼ä» ä¾æ¼ç¤ºä½¿ç¨ï¼å¤§å®¶å¨ç产ç¯å¢ä¸ä¸å®ä¸è¦è¿æ ·è®¾ç½®
å,java代ç 说æ
1,WebSecurityConfig.java
@[email protected] class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final static BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder(); @Resource private UserLoginFailureHandler userLoginFailureHandler;//ç»å½å¤±è´¥çå¤çç±» @Resource private UserLoginSuccessHandler userLoginSuccessHandler;//ç»å½æåçå¤çç±» @Resource private UserLogoutSuccessHandler userLogoutSuccessHandler;//éåºæåçå¤çç±» @Resource private UserAccessDeniedHandler userAccessDeniedHandler;//æ æ访é®çå¤çç±» @Resource private SecUserDetailService secUserDetailService; //ç¨æ·ä¿¡æ¯ç±»ï¼ç¨æ¥å¾å°UserDetails @Resource private CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource; @Resource private CustomAccessDecisionManager customAccessDecisionManager; //æå®å å¯çæ¹å¼ï¼é¿å
åºç°:There is no PasswordEncoder mapped for the id "null" @Bean public PasswordEncoder passwordEncoder(){//å¯ç å å¯ç±» return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { //éè¿æ°æ®åºçé
ç½®ï¼å¨æå¤æå½åç¨æ·æ¯å¦å¯ä»¥è®¿é®å½åurl http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor() { @Override public O postProcess(O object) { object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); object.setAccessDecisionManager(customAccessDecisionManager); return object; } }); //login http.formLogin() .loginPage("/login/login") .loginProcessingUrl("/login/logined")//åéAjax请æ±çè·¯å¾ .usernameParameter("username")//请æ±éªè¯åæ° .passwordParameter("password")//请æ±éªè¯åæ° .failureHandler(userLoginFailureHandler)//éªè¯å¤±è´¥å¤ç .successHandler(userLoginSuccessHandler)//éªè¯æåå¤ç .permitAll(); //ç»å½é¡µé¢ç¨æ·ä»»æè®¿é® //logout http.logout() .logoutUrl("/login/logout") .logoutSuccessUrl("/login/logout") .logoutSuccessHandler(userLogoutSuccessHandler)//ç»åºå¤ç .deleteCookies("JSESSIONID") .clearAuthentication(true) .invalidateHttpSession(true) .permitAll(); //å
¶ä»ä»»ä½è¯·æ±,ç»å½åå¯ä»¥è®¿é® http.authorizeRequests().anyRequest().authenticated(); //accessdenied http.exceptionHandling().accessDeniedHandler(userAccessDeniedHandler);//æ æéæ¶çå¤ç //user detail http.userDetailsService(secUserDetailService); } @Resource public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(secUserDetailService).passwordEncoder(new PasswordEncoder() { @Override public String encode(CharSequence charSequence) { return ENCODER.encode(charSequence); } //å¯ç å¹é
ï¼çè¾å
¥çå¯ç ç»è¿å å¯ä¸æ°æ®åºä¸åæ¾çæ¯å¦ä¸æ · @Override public boolean matches(CharSequence charSequence, String s) { return ENCODER.matches(charSequence,s); } }); }}
2,CustomAccessDecisionManager.java
@Componentpublic class CustomAccessDecisionManager implements AccessDecisionManager { //æ¯è¾ç¨æ·çæéåurlæéè¦çæéï¼ç¡®å®æ¯å¦å¯ä»¥è®¿é® @Override public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { for (ConfigAttribute configAttribute : configAttributes) { //å¦ææ¯all,表示ææçé½å
è®¸è®¿é® if ("ALL".equals(configAttribute.getAttribute())) { return; } //æ¯å¦æ²¡ææéè¦æ± if ("ROLE_def".equals(configAttribute.getAttribute())) { if (authentication instanceof AnonymousAuthenticationToken) { System.out.println("å¿åç¨æ·"); throw new AccessDeniedException("æéä¸è¶³ï¼æ æ³è®¿é®ï¼"); } else { System.out.println("å
¶ä»ç±»åç¨æ·,å¯ä»¥è®¿é®"); return; } } Collection extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { String userRole = authority.getAuthority(); //æ°æ®åºä¸æ²¡æä¿åROLE_,è¿éæ·»å ä¸ String menuRole = "ROLE_"+configAttribute.getAttribute(); if (userRole.equals(menuRole)) { //System.out.println("è¿å
¥åºç¨ç³»ç»"); return; } } } throw new AccessDeniedException("æéä¸è¶³ï¼æ æ³è®¿é®ï¼"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class> clazz) { return true; }}
ç¨æ¥å¤æå½åç¨æ·æ¯å¦ææé访é®å½åçurl
3,CustomFilterInvocationSecurityMetadataSource.java
@Componentpublic class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { AntPathMatcher pathMatcher = new AntPathMatcher(); @Autowired private SysMenuService sysMenuService; //å¾å°ææçmenu, //æ¥è¯¢åºå¹é
å½åurlçæærole @Override public CollectiongetAttributes(Object object) throws IllegalArgumentException { String requestUrl = ((FilterInvocation) object).getRequestUrl(); List menus = sysMenuService.getMenus(); for (SysMenu menu : menus) { if (pathMatcher.match(menu.getPattern(), requestUrl)) { List roles = menu.getRoles(); String[] roleStr = new String[roles.size()]; for (int i = 0; i < roles.size(); i++) { roleStr[i] = roles.get(i); } return SecurityConfig.createList(roleStr); } } return SecurityConfig.createList("ROLE_def"); } @Override public CollectiongetAllConfigAttributes() { return null; } @Override public boolean supports(Class> clazz) { return true; }}
éè¿æ¥è¯¢æ°æ®åºå¾å°å¹é å½åurlçrole
4,SecUser.java
public class SecUser extends User { //ç¨æ·id private int userid; //ç¨æ·æµç§° private String nickname; public SecUser(String username, String password, Collection extends GrantedAuthority> authorities) { super(username, password, authorities); } public SecUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection extends GrantedAuthority> authorities) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getUserid() { return userid; } public void setUserid(int userid) { this.userid = userid; }}
spring securityä¸Userç±»çåç±»ï¼å¢å äºç¨æ·idåæµç§°ï¼
éè¦ä¿åå°sessionä¸çä¿¡æ¯ï¼å¨è¿éæ©å±
ç®çæ¯é¿å å¨æ¯ä¸ªé¡µé¢ä¸æ¾ç¤ºç¨æ·ä¿¡æ¯éè¦æ¥æ°æ®åº
5,SecUserDetailService.java
/** * Created by liuhongdi on 2020/07/09.*/@Component("SecUserDetailService")public class SecUserDetailService implements UserDetailsService{ @Resource private SysUserService sysUserService; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //æ¥åº SysUser oneUser = sysUserService.getOneUserByUsername(s);//æ°æ®åºæ¥è¯¢ çç¨æ·æ¯å¦åå¨ String encodedPassword = oneUser.getPassword(); Collection collection = new ArrayList<>();//æééå //ç¨æ·æéï¼éè¦å ROLE_ List<String> roles = oneUser.getRoles(); //System.out.println(roles); for (String roleone : roles) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+roleone); collection.add(grantedAuthority); } //å¢å ç¨æ·çuserid,nickname SecUser user = new SecUser(s,encodedPassword,collection); user.setUserid(oneUser.getUserId()); user.setNickname(oneUser.getNickName()); return user; }}
6,UserAccessDeniedHandler.java
@Component("UserAccessDeniedHandler")public class UserAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { boolean isAjax = ServletUtil.isAjax(); //System.out.println("isajax:"+isAjax); if (isAjax == true) {ServletUtil.printRestResult(RestResult.error(ResponseCode.ACCESS_DENIED)); } else {ServletUtil.printString(ResponseCode.ACCESS_DENIED.getMsg()); } }}
7,UserLoginFailureHandler.java
@Component("UserLoginFailureHandler")public class UserLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { //System.out.println("UserLoginFailureHandler"); ServletUtil.printRestResult(RestResult.error(ResponseCode.LOGIN_FAIL)); }}
8,UserLoginSuccessHandler.java
@Component("UserLoginSuccessHandler")public class UserLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //System.out.println("UserLoginSuccessHandler"); ServletUtil.printRestResult(RestResult.success(0,"ç»å½æå")); }}
9,UserLogoutSuccessHandler.java
@Component("UserLogoutSuccessHandler")public class UserLogoutSuccessHandler implements LogoutSuccessHandler{ @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletRequest.getSession().invalidate(); ServletUtil.printRestResult(RestResult.success(0,"éåºæå")); }}
10,WebInterceptor.java
@Componentpublic class WebInterceptor extends HandlerInterceptorAdapter { //å¦æviewä¸ä¸ºç©ºï¼æç»å½ä¿¡æ¯ä¼ éç»æ¨¡æ¿ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { if (modelAndView != null) { ModelMap modelMap = modelAndView.getModelMap(); SecUser currentUser = SessionUtil.getCurrentUser(); if (currentUser != null) { modelMap.addAttribute("is_login","1"); modelMap.addAttribute("login_username",currentUser.getNickname()); } else { modelMap.addAttribute("is_login","0"); modelMap.addAttribute("login_username",""); } } }}
è´è´£æä¼ é页é¢å ¬å ±é¨åæ¾ç¤ºçæ°æ®å°æ¨¡æ¿
11,login.html
<html><head> <meta content="text/html;charset=UTF-8"/> <title>ç»å½é¡µé¢title> <script type="text/javascript" language="JavaScript" src="/js/jquery-1.6.2.min.js">script> <style type="text/css"> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; }style> <meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_header" th:content="${_csrf.headerName}"/>head><body><nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="/home/home" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" > é¦é¡µ a>li> ul> div> div>nav><div class="container"> <div class="starter-template"> <h2>使ç¨è´¦å·å¯ç ç»å½h2> <div class="form-group"> <label for="username">è´¦å·label> <input type="text" class="form-control" id="username" name="username" value="" placeholder="è´¦å·" /> div> <div class="form-group"> <label for="password">å¯ç label> <input type="password" class="form-control" id="password" name="password" placeholder="å¯ç " /> div> <button name="formsubmit" value="ç»å½" onclick="go_login()" >ç»å½button> div>div><script> function go_login(){ if ($("#username").val() == "") { alert('ç¨æ·åä¸å¯ä¸ºç©º'); $("#username").focus(); return false; } if ($("#password").val() == "") { alert('å¯ç ä¸å¯ä¸ºç©º'); $("#password").focus(); return false; } var postdata = { username:$("#username").val(), password:$("#password").val(), } var csrfToken = $("meta[name='_csrf']").attr("content"); var csrfHeader = $("meta[name='_csrf_header']").attr("content"); $.ajax({ type:"POST", //type:"GET", url:"/login/logined", data:postdata, //è¿åæ°æ®çæ ¼å¼ datatype: "json",//"xml", "html", "script", "json", "jsonp", "text". beforeSend: function(request) { request.setRequestHeader(csrfHeader, csrfToken); // æ·»å CSRF Token }, success:function(data){ if (data.code == 0) { alert('login success:'+data.msg); window.location.href="/home/home" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" ; } else { alert("failed:"+data.msg); } }, //è°ç¨æ§è¡åè°ç¨çå½æ° complete: function(XMLHttpRequest, textStatus){ }, //è°ç¨åºéæ§è¡çå½æ° error: function(){ //请æ±åºéå¤ç alert('error'); } }); }script>body>html>
12,页é¢ä¸ç¨å°çå ¶ä»ä»£ç ï¼å¯ä»¥ç§»æ¥github.comä¸æ¥ç
äºï¼æµè¯ææ
1,访é®é¦é¡µ:
http://127.0.0.1:8080/home/home
æªç»å½æ¶ï¼
2ï¼ä»¥æ®éç¨æ·lhdç»å½:
访é®:管çåé¦é¡µ/åæ·é¦é¡µï¼é½ä¼å¾å°æ示
æ æ访é®
访é®ä¿®æ¹å¯ç 页é¢ï¼å¯ä»¥è®¿é®
3,以merchantç¨æ·ç»å½ï¼
  roleæ¯MERCHANT
访é®:管çåé¦é¡µï¼æ示:
æ æ访é®
访é®åæ·é¦é¡µ:å¯ä»¥è®¿é®
访é®ä¿®æ¹å¯ç 页é¢ï¼å¯ä»¥è®¿é®Â
4ï¼ä»¥adminç¨æ·ç»å½:
roleæ¯ADMIN
访é®ç®¡çåé¦é¡µ:å¯ä»¥è®¿é®
访é®åæ·é¦é¡µ:å¯ä»¥è®¿é®
访é®ä¿®æ¹å¯ç 页é¢ï¼å¯ä»¥è®¿é®
5,ä»mysqlæ°æ®åºsys_menu_role表ä¸å é¤ï¼meuid=5  rolename=ADMIN
è¿æ¡è®°å½ï¼
åç¨adminç»å½åä¹ä¸è½å访é®åæ·é¦é¡µï¼
æ æ访é®
æ ééå¯åºç¨
å ï¼æ¥çspring bootççæ¬:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.3.RELEASE)
-
æ°æ¬¾SpringBootå¨çº¿æè²å¹³å°å¼æºäº
-
ç²¾åå¸å大æ±æ»
-
ä¸æâä¹è§éâè½»æ¾æå®é«å¹¶åä¸çå¹çæ§é®é¢(éè§é¢æç¨)
-
ä¸æææJava8 Lambda表达å¼(éè§é¢æç¨)
æè°¢ç¹èµæ¯æä¸åÂ