SpringBoot 集成Spring security
Spring security作为一种安全框架,使用简单,能够很轻松的集成到springboot项目中,下面讲一下如何在SpringBoot中集成Spring Security.使用gradle项目管理工具。
准备数据,
CREATE TABLE `user` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `roles` varchar(200) DEFAULT 'USER', PRIMARY KEY (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; insert into `user` values(0,'admin','admin','SUPER,role'); //给不同个用户配置不同的权限 insert into `user` values(1,'role','role','role');
1:配置buildgradle,添加spring boot插件和spring security
buildscript { repositories { mavenCentral() } dependencies { classpath('org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE') } } group "com.li" version "1.0-SNAPSHOT" apply plugin: "java" //java 插件 apply plugin: "org.springframework.boot" //spring boot 插件 apply plugin: "io.spring.dependency-management" apply plugin: "application" //应用 mainClassName = "com.li.SpringBootShrioApplication" sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile("org.springframework.boot:spring-boot-starter-web", "org.springframework.boot:spring-boot-starter-activemq", "org.springframework.boot:spring-boot-starter-test", "org.springframework.boot:spring-boot-starter-cache", "org.springframework.boot:spring-boot-devtools", "mysql:mysql-connector-java:5.1.35", 'org.apache.commons:commons-lang3:3.4', 'org.apache.commons:commons-pool2', "org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.0", 'org.apache.logging.log4j:log4j-core:2.7', 'org.springframework.boot:spring-boot-starter-security', 'org.springframework.boot:spring-boot-starter-thymeleaf', 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4', //thymeleaf模板,集成 springsecurity4
'net.sourceforge.nekohtml:nekohtml' ) testCompile group: 'junit', name: 'junit', version: '4.12' }
2:配置spring boot, application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springsecurity
username: root
password: 1367356
thymeleaf:
mode: LEGACYHTML5
cache: false
devtools:
restart:
enabled: true
server:
port: 8081
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml #Mapper所在的配置文件路径,进行扫描
config-location: classpath:mybatis/mybatis-config.xml # mybaits-config文件
3:配置Spring Security
WebSecurityConfig.java 继承 WebSecurityConfigurerAdapter.
当访问项目时,安全管理器按照配置进行拦截,验证通过才能访问Controller相应的路径。
package com.li.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Autowired MyUserDetailsService myUserDetailsService; @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(myUserDetailsService); authenticationProvider.setPasswordEncoder(this.bCryptPasswordEncoder()); return authenticationProvider; } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(this.bCryptPasswordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { // String[] s=null; http.csrf().disable() .authorizeRequests() .antMatchers("/user/settings").authenticated() // order matters .antMatchers("/", "/js/**", "/css/**","/avatar/**", "/images/**", "/fonts/**", "/bootstrap-select/**", "/bootstrap-datetimepicker/**", "/custom/**", "/daterangepicker/**", "/chartjs/**").permitAll() // these paths are configure not to require any authentication .antMatchers("/post/**").permitAll() // all posts are allowed to be viewed without authentication // .antMatchers("/user/**").permitAll() // all user profiles are allowed to be viewed without authentication .antMatchers("/category/**").permitAll() // all categories are allowed to be viewed without authentication .antMatchers("/user/registration").permitAll() .antMatchers("/avatar/**").permitAll() // temp .antMatchers("/visitor/**").permitAll() // temp // .antMatchers("/admin/**").hasAnyRole("SUPER","USER") // .antMatchers("/admin/**"). // .antMatchers("/admin/**").hasAnyRole(s) .anyRequest().authenticated() // every request requires the user to be authenticated .and() .formLogin() .loginPage("/user/login") .permitAll() // login URL can be accessed by anyone .and() .logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutSuccessUrl("/?logout") .permitAll(); } }
验证:对访问用户进行验证
package com.li.security; import com.li.dao.User; import com.li.service.UserService; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class MyUserDetailsService implements UserDetailsService{ Logger logger = LogManager.getLogger(MyUserDetailsService.class); @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = this.userService.findUserByUserName(username); logger.debug(user.getUserName()+"密码"+user.getPassword()); if(null == user) { throw new UsernameNotFoundException("Can't find user by username: " + username); } List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>(); // grant roles to user for (String role : user.getRolesSet()) { logger.debug(role); grantedAuthorities.add(new SimpleGrantedAuthority(role)); //认证 } // user.setGrantedAuthorities(authorities); //用于登录时 @AuthenticationPrincipal 标签取值 return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), grantedAuthorities); //角色认证 } }
4: Controller ,Service,Dao层编写
验证通过,Controller对相应的http处理。可以在每个http上面指定相应的访问权限
package com.li.controller; import com.li.dao.User; import com.li.service.UserService; import org.apache.catalina.servlet4preview.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.config.ResourceNotFoundException; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import javax.validation.Valid; import java.util.Map; @Controller public class UserController { @Autowired UserService userService; @RequestMapping(value = "/", method = RequestMethod.GET) public String login() { System.out.println("home"); return "forum/home"; } @RequestMapping(value = "/user/login", method = RequestMethod.GET) public String displayLoginPage(Model model) { System.out.println("进入"); model.addAttribute("title", "用户登陆"); return "forum/user-login"; //登录界面,验证没通过。 } /** * 用户注册 * @param model * @return */ @RequestMapping(value = "/user/registration", method = RequestMethod.GET) public String showRegistrationPage(Model model) { System.out.println("registrationGet"); model.addAttribute("userDto", new User()); return "forum/user-registration"; //注册页面 } @RequestMapping(value = "/user/registration", method = RequestMethod.POST) //提交注册 public String registerNewUser(@ModelAttribute("userDto") User user,BindingResult bindingResult, Model model, HttpServletRequest request) { System.out.println("registrationPost"); Map<String, Object> attributes = this.userService.registerUserAccount(user); model.addAllAttributes(attributes); return "forum/user-registration-result"; } @PreAuthorize("hasAuthority('SUPER')") //需要SUPER用户才能通过该路径,第一步通过配置验证,没有用户登录,将会拦截,让用户登录,登录成功,访问该路径时进行角色验证。 @RequestMapping(value = "/admin/admin", method = RequestMethod.GET) public String deletePost() { return "admin/admin"; } }
Service
package com.li.service; import com.li.dao.User; import javax.servlet.http.HttpServletRequest; import java.util.Map; public interface UserService { public User findUserByUserName(String userName); public Map<String, Object> registerUserAccount(User user); }
SeriviceImpl,对用户密码加密存储,
package com.li.service.impl; import com.li.dao.User; import com.li.dao.mapper.UserMapper; import com.li.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.sql.Timestamp; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.UUID; @Service public class UserServiceImpl implements UserService{ @Autowired BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired UserMapper userMapper; @Override public User findUserByUserName(String userName) { return userMapper.findByUserName(userName); } @Override public Map<String, Object> registerUserAccount(User userDto) { Map<String, Object> attributes = new HashMap<>(); // save newly registered user User user = new User(); user.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); //保存时应该将密码编码 user.setUserName(userDto.getUserName()); // user.activated(true); user.setRoles("USER"); // user.setConfirmationToken(UUID.randomUUID().toString()); // save new user and get number of affected row // logger.debug("用户注册"); int affectedRow = userMapper.save(user);// populate attributes String registrationResult = affectedRow == 1 ? "success" : "failure"; attributes.put("userRegistrationResult", registrationResult); return attributes; } }
DaoMapper
package com.li.dao.mapper; import com.li.dao.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @Mapper public interface UserMapper { public User findByUserName(String username); public int save(@Param("user") User user); }
普通User类
package com.li.dao; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * 实验室网站用户 */ public class User { public int id; public String userName; public String password; public String roles; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRoles() { return roles; } public void setRoles(String roles) { this.roles = roles; } public Set<String> getRolesSet() { //获取用户权限 if (null == roles) { return null; } return Collections.unmodifiableSet( new HashSet<String>(Arrays.asList(getRoles().split(",")))); } public void addRole(String role) { String currRoles = this.getRoles(); if (null == currRoles || this.getRoles().contains(role)) { return; } this.setRoles(currRoles + "," + role); } @Override public String toString() { return "User{" + "id=" + id + ", userName='" + userName + '\'' + ", password='" + password + '\'' + ", roles='" + roles + '\'' + '}'; } }
5:编写项目的前台页面,static文件夹下面,不同权限的页面放到不同种类下面
本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究