SpringBoot2.0整合SpringSecurity並實現驗證碼和許可權控制
阿新 • • 發佈:2018-12-09
使用SpringBoot2.0 整合SpringSecurity加入kaptcha驗證碼,使用redis實現session共享,請看原始碼,附上資料庫指令碼,絕對可以跑起來,原始碼地址為
1、pom檔案
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xueqing.demo</groupId> <artifactId>spring-boot-security</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-boot-security</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <argLine>-Dfile.encoding=UTF-8</argLine> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--jdbc支援--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--加入springboot對mybatis的支援--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!--使用Spring快取--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--spring快取使用redis進行快取--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--Session使用redis快取--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!--引入spring security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 引入thymeleaf的依賴包. --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!--kaptcha驗證碼生成器--> <dependency> <groupId>com.github.axet</groupId> <artifactId>kaptcha</artifactId> <version>0.0.9</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、配置SecurityConfig
package com.xueqing.demo.springbootsecurity.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.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.BeanIds; 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; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) //開啟方法許可權控制 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(customUserDetailsService()) .passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 所有使用者均可訪問的資源 .antMatchers( "/favicon.ico","/css/**","/common/**","/js/**","/images/**","/captcha.jpg","/login","/userLogin","/login-error").permitAll() // 任何尚未匹配的URL只需要驗證使用者即可訪問 .anyRequest().authenticated() .and() .formLogin().loginPage("/login").successForwardUrl("/index").failureForwardUrl("/login?error=1") .and() //許可權拒絕的頁面 .exceptionHandling().accessDeniedPage("/403"); http.logout().logoutSuccessUrl("/login"); } /** * 設定使用者密碼的加密方式 * @return */ @Bean public Md5PasswordEncoder passwordEncoder() { return new Md5PasswordEncoder(); } /** * 自定義UserDetailsService,授權 * @return */ @Bean public CustomUserDetailsService customUserDetailsService(){ return new CustomUserDetailsService(); } /** * AuthenticationManager * @return * @throws Exception */ @Bean(name = BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
3.、自定義UserDetailsService實現使用者的認證和授權
package com.xueqing.demo.springbootsecurity.security; import com.xueqing.demo.springbootsecurity.bean.Menu; import com.xueqing.demo.springbootsecurity.bean.User; import com.xueqing.demo.springbootsecurity.service.MenuService; import com.xueqing.demo.springbootsecurity.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; 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.Collection; import java.util.List; /** * 認證和授權 */ @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserService userService; @Autowired private MenuService menuService; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //--------------------認證賬號 User user = userService.loadUserByUsername(s); if (user == null) { throw new UsernameNotFoundException("賬號不存在"); } //-------------------開始授權 List<Menu> menus = menuService.getMenusByUserId(user.getId()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); for (Menu menu : menus) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(menu.getUrl()); //此處將許可權資訊新增到 GrantedAuthority 物件中,在後面進行全許可權驗證時會使用GrantedAuthority 物件。 grantedAuthorities.add(grantedAuthority); } user.setAuthorities(grantedAuthorities); return user; } }
4、自定義密碼比較器
package com.xueqing.demo.springbootsecurity.security;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 自定義密碼比較器 3
* 在此 密碼我就不加密了
*/
public class Md5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence);
}
}
5、自定義SpringSecurityUtils工具類
package com.xueqing.demo.springbootsecurity.security;
import com.xueqing.demo.springbootsecurity.bean.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import java.util.Collection;
public class SecurityUtils {
public static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
public static Collection<? extends GrantedAuthority> getAllPermission(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
return authorities;
}
public static boolean hasPermission(String permission){
if(StringUtils.isEmpty(permission)){
return false;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean hasPermission = false;
for(GrantedAuthority grantedAuthority : authorities){
String authority = grantedAuthority.getAuthority();
if(authority.equals(permission)){
hasPermission =true;
}
}
return hasPermission;
}
public static User getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (User) authentication.getPrincipal();
}
public static void logout(){
SecurityContextHolder.clearContext();
}
}
6、在登陸方法中,使用AuthenticationManager攔截登陸請求
package com.xueqing.demo.springbootsecurity.controller;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.xueqing.demo.springbootsecurity.bean.R;
import com.xueqing.demo.springbootsecurity.bean.User;
import com.xueqing.demo.springbootsecurity.service.UserService;
import com.xueqing.demo.springbootsecurity.util.ConstantVal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
@Controller
public class LoginController {
@Autowired
private AuthenticationManager myAuthenticationManager;
@Autowired
DefaultKaptcha defaultKaptcha;
@RequestMapping(value = "/userLogin")
public String userLogin(HttpServletRequest request) {
User userInfo = new User();
String username = request.getParameter("username");
String password = request.getParameter("password");
String kaptcha = request.getParameter("kaptcha");
userInfo.setUsername(username);
userInfo.setPassword(password);
String s = request.getSession().getAttribute(ConstantVal.CHECK_CODE).toString();
if (StringUtils.isEmpty(kaptcha) || !s.equals(kaptcha)) {
return "redirect:login-error?error=1";
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
try{
//使用SpringSecurity攔截登陸請求 進行認證和授權
Authentication authenticate = myAuthenticationManager.authenticate(usernamePasswordAuthenticationToken);
SecurityContextHolder.getContext().setAuthentication(authenticate);
//使用redis session共享
HttpSession session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); // 這個非常重要,否則驗證後將無法登陸
}catch (Exception e){
e.printStackTrace();
return "redirect:login-error?error=2";
}
return "redirect:index";
}
@RequestMapping("/captcha.jpg")
@ResponseBody
public R applyCheckCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
R r = new R();
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
//生成文字驗證碼
String text = defaultKaptcha.createText();
//生成圖片驗證碼
BufferedImage image = defaultKaptcha.createImage(text);
//儲存到session
request.getSession().setAttribute(ConstantVal.CHECK_CODE, text);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "jpg", out);
return r;
}
}
7、我的user表中有兩個使用者一個admin,一個user,admin具有所有許可權,而user不具有操作許可權
user登陸的介面為
admin登陸的介面為
user使用者操作使用者新增、刪除、修改、都改顯示沒許可權,而admin可以