SpringBoot 集成Spring security

时间:2021-06-10 20:26:34

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文件夹下面,不同权限的页面放到不同种类下面