1. 程式人生 > 其它 >SpringBoot+Spring security+Mysql

SpringBoot+Spring security+Mysql

技術標籤:# Sprng Security

這裡寫自定義目錄標題


專案格式
---------------承上啟下線------------
在這裡插入圖片描述

1.匯入pom配置檔案

<
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>2020</groupId> <
artifactId
>
HandlerInterceptor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>HandlerInterceptor</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding
>
</properties> <!-- 管理bean --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--jsp介面jstl依賴(下拉選擇) --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- servlet 依賴 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <!--使jsp頁面生效,進行訪問 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- spring-boot-starter-security依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 啟動shiro --> <!-- <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.4.0</version> </dependency> --> <!-- 整合Thymeleaf --> <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> --> <!--通用mapper,tk.mapper --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>1.1.4</version> </dependency> <!--對資料庫的支援 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--資料庫mysql的支援 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--json的支援 --> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency> <!-- @Data依賴 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> </dependencies> <build> <finalName>HandlerInterceptor</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!-- 設定編碼為 UTF-8 --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>

2.配置全域性配置檔案(application.properties)

#jsp頁面處理
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp


#MYSQL資料庫連線
spring.jpa.database = MYSQL
spring.datasource.url = jdbc:mysql://localhost/hand?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username =賬號
spring.datasource.password =密碼
#spring.datasource.password =密碼
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
mybatis.typeAliasesPackage=com.hand.pojo
mybatis.mapperLocations=classpath:mappers/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false

3.啟動類:HandWeb.java

package com.hand.web;

import org.mybatis.spring.annotation.MapperScan;
//import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;


@SpringBootApplication
@ComponentScan("com.hand")
@MapperScan("com.hand.mapper")
@EnableTransactionManagement//開啟事務---可以不寫
public class HandWeb {
	public static void main(String[] args) {
		SpringApplication.run(HandWeb.class, args);
	}
	
}

4.配置Security的介面卡

package com.hand.config;

import java.io.IOException;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hand.service.UserService;

/**
 * 介面卡
 * Security的認證策略
 * @author DoubleC
 *
 */
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	UserService userService;
	@Autowired
	CustomExpiredSessionStrategy customExpiredSessionStrategy;

	/*
	 * @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
	 */
	/**
	 * 密碼進行加密
	 * @return
	 */
	@Bean 
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder(10);
		//return NoOpPasswordEncoder.getInstance();
	}
	

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
		//super.configure(auth);
//		auth.inMemoryAuthentication()
//			.withUser("root").password("909396").roles("ADMIN","DBA")
//			.and()
//			.withUser("admin").password("909396").roles("ADMIN","USER")
//			.and()
//			.withUser("sang").password("909396").roles("USER");
		/**
		 * 將使用者與資料庫進行對接,並將密碼加密匹配
		 */
		auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		//super.configure(http);
		http
		.authorizeRequests()
			/**
			 * 資源管理許可權配置
			 */
			.antMatchers("/static/**").permitAll()
			.antMatchers("/images/**").permitAll()
			.antMatchers("/WEB/**").permitAll()
			.antMatchers("/dynamic/**").access("hasAnyRole('admin','dba','user')")
			
			//.access("hasRole('ADMIN') and hasRole('DBA') and hasRole('USER')")
			//.antMatchers("/user/**").permitAll()
			.antMatchers("/admin/**")
			.hasRole("admin")
			.antMatchers("/user/**")
			.access("hasAnyRole('admin','dba','user')")
			//.access("hasRole('admin') and hasRole('user')")
			.antMatchers("/db/**")
			.access("hasRole('ADMIN') and hasRole('DBA')")
			
			.anyRequest()
			.authenticated()
			.and()
			/**
			 * 登入表單詳細配置
			 */
			.formLogin()
			.loginPage("/login")
			.loginProcessingUrl("/user/login")
				/*
				 * .usernameParameter("username") .passwordParameter("password")
				 */
			.permitAll()
			/**
			 * 登入成功實現資料互動
			 */
			.successHandler(new AuthenticationSuccessHandler() {
				
				@Override
				public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
						Authentication auth) throws IOException, ServletException {
					// TODO Auto-generated method stub
					/**
					 * 前後端資料互動配置
					 */
					Object principal = auth.getPrincipal();
					response.setContentType("application/json;charset=utf-8");
					PrintWriter out = response.getWriter();
					response.setStatus(200);
					Map<String, Object> map = new HashMap<>();
					map.put("status", 200);
					map.put("msg", principal);
					ObjectMapper om = new ObjectMapper();
					out.write(om.writeValueAsString(map));
					out.flush();
					out.close();
				}
			})
			/**
			 * 登入失敗進行資料互動
			 */
			.failureHandler(new AuthenticationFailureHandler() {
				
				@Override
				public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
						AuthenticationException e) throws IOException, ServletException {
					// TODO Auto-generated method stub
					response.setContentType("application/json;charset=utf-8");
					PrintWriter out = response.getWriter();
					response.setStatus(401);
					Map<Object, Object> map = new HashMap<>();
					map.put("status", 401);
					if(e instanceof LockedException) {
						map.put("msg", "賬戶被鎖定,登入失敗!");
					}else if(e instanceof BadCredentialsException) {
						map.put("msg", "賬戶或密碼輸入錯誤,登入失敗!");
					}else if(e instanceof DisabledException) {
						map.put("msg", "賬戶被禁用,登入失敗");
					}else if(e instanceof AccountExpiredException) {
						map.put("msg", "賬戶已過期,登入失敗");
					}else if(e instanceof CredentialsExpiredException) {
						map.put("msg", "密碼已過期,登入失敗");
					}else {
						map.put("msg", "登入失敗");
					}
					ObjectMapper om = new ObjectMapper();
					out.write(om.writeValueAsString(map));
					out.flush();
					out.close();
				}
			})
			/**
			 * 登出登入配置
			 */
			.and()
			.logout()
			.logoutUrl("/logout")
			.clearAuthentication(true)
			.invalidateHttpSession(true)
			.addLogoutHandler(new LogoutHandler() {

				@Override
				public void logout(HttpServletRequest request, HttpServletResponse response,
						Authentication auth) {
					// TODO Auto-generated method stub
					
				}
				
			})
			.logoutSuccessHandler(new LogoutSuccessHandler() {

				@Override
				public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
						Authentication auth) throws IOException, ServletException {
					// TODO Auto-generated method stub
					//登出以後返回介面
					response.sendRedirect("/login_page");
				}
				
			})
			.permitAll()
			/**
			 * 解決jsp頁面iframe全部失效問題
			 * X-Frame-Options' to 'deny'故障
			 * iframe全部失效了
			 * index:
			 * 1 Refused to display 
			 * 'http://localhost:8080/dynamic/page/ecg.jsp'
			 *  in a frame because it set 'X-Frame-Options' to 'deny'.
			 */
			.and()
			.headers().frameOptions().disable()
			
			.and()
			/**
			 * 解決賬號同時線上問題,在同一時間只允許一個一臺機器線上
			 */
			.sessionManagement().maximumSessions(1).expiredSessionStrategy(customExpiredSessionStrategy).expiredUrl("/login");
			http
			.csrf()
			.disable();
			
			http.headers().cacheControl();
			//http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
			
	        http.logout().logoutSuccessUrl("/login");
			http.rememberMe().rememberMeParameter("remember");
		
	}
}

4.1(UserService)

UserService.java

package com.hand.service;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
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 com.hand.mapper.UserMapper;
import com.hand.pojo.User;
@Service
public class UserService implements UserDetailsService{
	@Autowired
	UserMapper userMapper;
	@Autowired
	HttpSession session;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		//根據使用者名稱獲取使用者的基本資訊
		User user=userMapper.loadUserByUsername(username);
		session.setAttribute("user", user);
//		UsernamePasswordToken token = new UsernamePasswordToken();
//		Subject subject = SecurityUtils.getSubject();
//		try {
//			subject.login(token);
//		} catch (Exception e) {
//			// TODO: handle exception
//		}

		if(user==null||user.equals(" ")) {
			System.out.println("賬號或密碼錯誤!");
			throw new UsernameNotFoundException("賬戶不存在!");
		}
		//獲取使用者的許可權,並將使用者許可權表嵌入user中
		user.setRoles(userMapper.getUserRolesByUid(user.getId()));
		return user;
	}
	
}

4.2(CustomExpiredSessionStrategy)

CustomExpiredSessionStrategy.java(不是必須類,可要可不要)

package com.hand.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;

import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class CustomExpiredSessionStrategy implements SessionInformationExpiredStrategy {

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("code",0);
        map.put("msg","已經另一臺機器登入,您被迫下線。" + event.getSessionInformation().getLastRequest());
        String s = objectMapper.writeValueAsString(map);
        event.getResponse().setContentType("application/json;charset=UTF-8");
        event.getResponse().getWriter().write(s);
        event.getResponse().sendRedirect("/login");
    }
}

4.3(RegService)

RegService.java (使用者註冊,給密碼進行加密,存入資料庫)

package com.hand.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.hand.mapper.UserMapper;
import com.hand.pojo.User;

@Service
public class RegService {
	@Autowired
	UserMapper userMapper;
	public Boolean reg(String username,String password) {
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);
		String encodePassword=encoder.encode(password);
		return saveToOb(username,encodePassword);
	}

	private Boolean saveToOb(String username, String encodePassword) {
		User user = new User();
		user.setUsername(username);
		user.setPassword(encodePassword);
		/**
		 * 註冊新使用者
		 * 根據使用者名稱查詢使用者ID
		 * 根據使用者ID給使用者賦予許可權最低許可權(使用者許可權)
		 */
		Integer userId=userMapper.userInsert(username,encodePassword);
		Integer uid=userMapper.getUserId(username);
		Integer user_roldId=userMapper.insertUserRole(uid);
		if(userId>=1) {
			if(user_roldId>=1)
				return true;
			return false;
		}
		return false;
	}
}

4.4(UserMapper )

package com.hand.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.hand.common.MyMapper;
import com.hand.pojo.Role;
import com.hand.pojo.User;

public interface UserMapper extends MyMapper<User>{

	User loadUserByUsername(String username);

	List<Role> getUserRolesByUid(String id);

	int userInsert(@Param("username")String username, @Param("password")String encodePassword);

	Integer getUserId(String username);

	Integer insertUserRole(Integer id);


}

4.5 (Role )

package com.hand.pojo;

public class Role {
	private Integer id;
	private String name;
	private String namezh;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getNamezh() {
		return namezh;
	}
	public void setNamezh(String namezh) {
		this.namezh = namezh;
	}
	@Override
	public String toString() {
		return "Role [id=" + id + ", name=" + name + ", namezh=" + namezh + "]";
	}
	
}

4.6(User )

package com.hand.pojo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.Id;
import javax.persistence.Transient;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;


public class User implements UserDetails,Serializable{
	@Id
	private String id;
	private String username;
	private String password;
	private Boolean enabled;
	private Boolean locked;
	@Transient
	private List<Role> roles;

	/*
	 * public User(Integer id, String username, String password, Boolean enabled,
	 * Boolean locked) { // TODO Auto-generated constructor stub
	 * this.username=username; this.password=password; this.enabled=enabled;
	 * this.locked=locked; }
	 */
	

	public String getId() {
		return id;
	}

	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public void setId(String id) {
		this.id = id;
	}

	/*
	 * public Boolean getEnabled() { return enabled; }
	 * 
	 * public void setEnabled(Boolean enabled) { this.enabled = enabled; }
	 */

	public Boolean getLocked() {
		return locked;
	}

	public void setLocked(Boolean locked) {
		this.locked = locked;
	}

	

	public List<Role> getRoles() {
		return roles;
	}

	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + ", enabled=" + enabled
				+ ", locked=" + locked + ", roles=" + roles + "]";
	}





	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		List<SimpleGrantedAuthority> authorities = new ArrayList<>();
		for(Role role:roles) {
			authorities.add(new SimpleGrantedAuthority(role.getName()));
		}
		return authorities;
	}



	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return !locked;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		/*
		 * if (StringUtils.isEmpty(getEnabled()) ) { return !enabled; }
		 */
		return enabled;
	}
	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return username.hashCode();
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		return this.toString().equals(obj.toString());
	}
	

}

5.書寫SQL語句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hand.mapper.UserMapper">
	<select id="loadUserByUsername" resultType="com.hand.pojo.User">
		select * from user where username=#{username}
	</select>
	<select id="getUserRolesByUid" resultType="com.hand.pojo.Role">
		select * from role r,user_role ur where r.id=ur.rid and ur.uid=#{id}
	</select>
	<insert id="userInsert">
		insert into user(id,username,password,enabled,locked) values(null,#{username},#{password},1,0);
	</insert>
	<select id="getUserId" resultType="java.lang.Integer">
		select id from user where username=#{username}
	</select>
	<!-- 給使用者賦予最低等許可權3(普通使用者許可權) -->
	<insert id="insertUserRole">
		insert into user_role(id,uid,rid) values(null,#{id},3)
	</insert>
	
</mapper> 

6.資料表構造

CREATE TABLE USER(
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY UNIQUE,
username VARCHAR(32) NOT NULL UNIQUE ,
PASSWORD VARCHAR(255),
enabled TINYINT(1) NOT NULL,
locked TINYINT(1) NOT NULL
) ENGINE =INNODB DEFAULT CHARSET=utf8;

CREATE TABLE role(
id INT(11) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL,
namezh VARCHAR(32) NOT NULL ,
PRIMARY KEY (id)
) ENGINE =INNODB DEFAULT CHARSET=utf8;




-- 許可權角色關係表--
CREATE TABLE user_role(
  id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY UNIQUE,
  uid INT(11) NOT NULL ,
  rid INT(11) NOT NULL,
  KEY idx_uid(uid),
  KEY idx_rid(rid)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

7.表資料展示

給使用者表插入幾條資料
許可權表
使用者與許可權關聯表