添加依賴
<!--數據庫相關-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置文件
spring:
messages:
basename: i18n/Messages,i18n/Pages
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
driver-class-name: org.gjt.mm.mysql.Driver # 配置MySQL的驅動程序類
url: jdbc:mysql://47.106.106.53:3306/security?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 # 數據庫連接地址
username: root # 數據庫用戶名
password: Rojao@123
spring.jpa.database: mysql
spring.jpa.database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
# 顯示後臺處理的SQL語句
spring.jpa.show-sql: true
# 自動檢查實體和數據庫表是否一致,如果不一致則會進行更新數據庫表
spring.jpa.hibernate.ddl-auto: create
spring.jpa.open-in-view: false
Security配置類
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 啓用方法級別的權限認證
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService myUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 允許所有用戶訪問"/"和"/index.html"
http.authorizeRequests()
.antMatchers("/", "/index.html").permitAll() //定義不需要認證就可以訪問
.antMatchers("/level1/**").hasRole("VIP1") //需要擁有VIP1權限
.anyRequest().authenticated() // 其他地址的訪問均需驗證權限
.and()
//開啓cookie保存用戶數據
.rememberMe()
//設置cookie有效期
.tokenValiditySeconds(60 * 60 * 24 * 7)
.and()
.formLogin() // 定義當需要用戶登錄時候,轉到的登錄頁面
.loginPage("/login.html") // 登錄頁
.failureUrl("/login-error.html").permitAll()
.and()
.logout()
.logoutSuccessUrl("/index.html");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/* auth.inMemoryAuthentication()
.withUser("admin").password("123456").roles("USER");*/
}
@Override
public void configure(WebSecurity web) throws Exception {
//靜態資源忽略認證
web.ignoring().antMatchers("/css/**");
}
/**
* 配置登錄驗證
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
return new MyPasswordEncoder();
}
}
通過@EnableWebSecurity註解開啓Spring Security的功能 繼承WebSecurityConfigurerAdapter,並重寫它的方法來設置一些web安全的細節 configure(HttpSecurity http)方法,通過authorizeRequests()定義哪些URL需要被保護、哪些不需要被保護。例如以上代碼指定了/和/home不需要任何認證就可以訪問,其他的路徑都必須通過身份驗證。 通過formLogin()定義當需要用戶登錄時候,轉到的登錄頁面。 configureGlobal(AuthenticationManagerBuilder auth)方法,在內存中創建了一個用戶,該用戶的名稱爲admin,密碼爲123456,用戶角色爲USER。
自定義密碼解析
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
//MD5Util.encode((String) charSequence);
System.out.println(charSequence.toString());
return null;
}
@Override
public boolean matches(CharSequence charSequence, String s) {
System.out.println(charSequence);
System.out.println(s);
return s.equals(charSequence.toString());;
}
}
指定了密碼的加密方式(5.0 版本強制要求設置),因爲我們數據庫是明文存儲的,所以明文返回即可
自定義UserDetailsService實現類
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private SysUserDao sysUserDao;
/**
* 授權的時候是對角色授權,認證的時候應該基於資源,而不是角色,因爲資源是不變的,而用戶的角色是會變的
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUserEntity userEntity = sysUserDao.findByUserName(username);
if (null == userEntity) {
throw new UsernameNotFoundException(username);
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
List<SysRoleEntity> roleList = userEntity.getRoleList();
if(roleList == null || roleList.size() == 0){
return new User(userEntity.getUserName(), userEntity.getPassword(), authorities);
}
for (SysRoleEntity role : roleList) {
List<SysPermissionEntity> permList = role.getPermissionEntityList();
if(permList == null){
continue;
}
for(SysPermissionEntity permission : permList){
//添加用戶的權限
authorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
}
return new User(userEntity.getUserName(), userEntity.getPassword(), authorities);
}
}
需要重寫 loadUserByUsername 方法,參數是用戶輸入的用戶名。返回值是UserDetails,這是一個接口,一般使用它的子類org.springframework.security.core.userdetails.User,它有三個參數,分別是用戶名、密碼和權限集。
自定義對 hasPermission()
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object targetPermission) {
// 獲得loadUserByUsername()方法的結果
User user = (User)authentication.getPrincipal();
// 獲得loadUserByUsername()中注入的角色
Collection<GrantedAuthority> authorities = user.getAuthorities();
for(GrantedAuthority authority : authorities){
if(authority.getAuthority().equals(targetPermission)){
return true;
}
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
return false;
}
}
@RestController
public class PermissionController {
@RequestMapping("/perm")
@PreAuthorize("hasPermission('/perm','perm')")
public String perm(){
return "success";
}
}
@PreAuthorize("hasPermission('/perm','perm')")
是關鍵,參數1指明瞭訪問該接口需要的url,參數2指明瞭訪問該接口需要的權限。
自定義Filter
public class MyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public MyAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
return null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String userName = (String) req.getSession().getAttribute("username");
if("test".equals(userName)){
super.unsuccessfulAuthentication(req, res,new InsufficientAuthenticationException("輸入的驗證碼不正確"));
}
// super.unsuccessfulAuthentication(req, res,new InsufficientAuthenticationException("輸入的驗證碼不正確"));
chain.doFilter(request, response);
}
}
實體類
@Data
@Entity
@Table(name = "sys_user")
public class SysUserEntity {
@Id
@GeneratedValue
@Column(name = "user_id")
private Long userId;
@Column(nullable = false,unique = true, length = 50)
private String userName;
@Column(nullable = false, length = 200)
private String password;
@ManyToMany(cascade = {CascadeType.ALL},fetch = FetchType.EAGER)
@JoinTable(name="sys_user_role_map",joinColumns={@JoinColumn(name="user_id")},inverseJoinColumns={@JoinColumn(name="role_id")})
List<SysRoleEntity> roleList;
}
@Data
@Entity
@Table(name = "sys_role")
public class SysRoleEntity {
@Id
@Column(name = "role_id")
private Long roleId;
@Column(nullable = false, length = 200)
private String roleName;
@ManyToMany(cascade = {CascadeType.ALL},fetch = FetchType.EAGER)
@JoinTable(name="sys_role_permission_map",joinColumns={@JoinColumn(name="role_id")},inverseJoinColumns={@JoinColumn(name="perm_id")})
List<SysPermissionEntity> permissionEntityList;
}
@Data
@Entity
@Table(name = "sys_permission")
public class SysPermissionEntity {
@Id
@Column(name = "perm_id")
private Long permId;
@Column(length = 30)
private String code;
}
DAO類
@Repository
public interface SysUserDao extends JpaRepository<SysUserEntity, Long> {
SysUserEntity findByUserName(String userName);
}