1.創建工程並引入坐標
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- 設定一個空值將始終從倉庫中獲取,不從本地路徑獲取 查找順序:relativePath元素中的地址–本地倉庫–遠程倉庫 --> </parent> <dependencies> <!-- web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringSecurity --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- jsp頁面使用jstl標簽 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- 用於編譯jsp --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <!-- Provided 編譯和測試的時候使用--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- 通用mapper --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.1.5</version> </dependency> </dependencies>
2.加入jsp頁面等靜態資源:
在src/main目錄下創建webapp目錄

這時webapp目錄並不能正常使用,因為只有web工程才有webapp目錄,在pom文件中修改項目為web工程

3.編寫控制器
@Controller @RequestMapping("/product") public class ProductController { @Secured({"ROLE_PRODUCT", "ROLE_ADMIN"}) // SpringSecurity內部制定的注解 // @RolesAllowed({"ROLE_PRODUCT", "ROLE_ADMIN"}) // jsr250注解 // @PreAuthorize("hasAnyRole('ROLE_PRODUCT', 'ROLE_ADMIN')") // spring的el表達式注解 @GetMapping("/findAll") public String findAll() { return "product-list"; } }
4.創建角色 pojo對象
這里直接使用SpringSecurity的角色規范,實現GrantedAuthority接口
public class SysRole implements GrantedAuthority { private Integer id; private String roleName; private String roleDesc; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleDesc() { return roleDesc; } public void setRoleDesc(String roleDesc) { this.roleDesc = roleDesc; } @JsonIgnore // 在將來我們可能會拿這個實體類轉成json字符串,或者json轉成java對象,需要忽略重寫的幾個屬性 @Override public String getAuthority() { return roleName; } }
5.創建用戶pojo對象
這里直接實現SpringSecurity的用戶對象接口UserDetails,並添加角色集合私有屬性。
public class SysUser implements UserDetails { private Integer id; private String username; private String password; // 在MySql中是沒有直接定義成Boolean這種數據類型. // 它只能定義成 tinyint(1) ; // status等於1時代表true,status等於0時代表false; private Boolean status; private List<SysRole> roles; public List<SysRole> getRoles() { return roles; } public void setRoles(List<SysRole> roles) { this.roles = roles; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public Boolean getStatus() { return status; } public void setStatus(Boolean status) { this.status = status; } @JsonIgnore // 在將來我們可能會拿這個實體類轉成json字符串,或者json轉成java對象,需要忽略重寫的幾個屬性 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } /** * 賬戶是否失效 * @return */ @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } /** * 賬戶是否鎖定 * @return */ @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } /** * 密碼是否失效 * @return */ @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } /** * 指定當前用戶是否可用 * @return */ @JsonIgnore @Override public boolean isEnabled() { return status; } }
6.提供角色mapper接口
public interface RoleMapper extends Mapper<SysRole> { @Select("select * from sys_role r, sys_user_role ur where r.id=ur.rid and ur.uid = #{uid}") List<SysRole> findByUid(Integer uid); }
7.提供用戶mapper接口
public interface UserMapper extends Mapper<SysUser> { @Select("select * from sys_user where username = #{username}") @Results({ @Result(id = true, property = "id", column = "id"), @Result(property = "username", column = "username"), @Result(property = "password", column = "password"), @Result(property = "roles", column = "id", many = @Many( select = "com.fgy.mapper.RoleMapper.findByUid", fetchType = FetchType.EAGER ) ) }) SysUser findByName(String username); }
8.提供認證service接口
public interface UserService extends UserDetailsService { }
9.提供認證 service實現類
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return userMapper.findByName(s);
}
}
10.自定義異常頁面
@ControllerAdvice public class HandlerControllerException { @ExceptionHandler(RuntimeException.class) public String exceptionHandler(RuntimeException e) { if (e instanceof AccessDeniedException) { // 如果是權限不足異常,則跳轉到權限不足頁面! return "redirect:/403.jsp"; } // 其余的異常都到500頁面! return "redirect:/500.jsp"; } }
11.編寫配置文件(.yml)
server: port: 8080 spring: mvc: view: prefix: /pages/ suffix: .jsp datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql:///springsecurity username: root password: root mybatis: type-aliases-package: com.fgy.domain # mybatis默認是屬性名和數據庫字段名一一對應的,即 # 數據庫表列:user_name # 實體類屬性:user_name # 但是java中一般使用駝峰命名 # 數據庫表列:user_name # 實體類屬性:userName # 在Springboot中,可以通過設置map-underscore-to-camel-case屬性為true來開啟駝峰功能 configuration: map-underscore-to-camel-case: true logging: level: com.fgy: debug
12.提供 SpringSecurity 配置類,放在config包下
/** * springSecurity配置類 */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true, securedEnabled = true) // 實際只開一種即可,這里只是演示 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; /** * 生成認證使用的加密對象,添加到容器中 * @return */ @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 認證用戶的來源【內存或數據庫】 @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { // 指定用戶的來源,一般不會這么做,這里只是測試 /*auth.inMemoryAuthentication() .withUser("user") .password("{noop}user") .roles("USER");*/ auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } // 配置springSecurity相關信息 @Override public void configure(HttpSecurity http) throws Exception { // 釋放靜態資源,指定資源攔截規則,指定自定義認證頁面,指定退出認證配置,csrf配置 http.authorizeRequests() .antMatchers("/login.jsp", "/failer.jsp", "/css/**", "/img/**", "/plugins/**") .permitAll() //釋放上面的資源,不需要認證即可訪問 .antMatchers("/**").hasAnyRole("ROLE_USER", "ROLE_ADMIN") .anyRequest().authenticated() // .anyRequest().authenticated() 表示其他請求資源需要認證之后才能訪問 .and() // 表示一個新的配置開始(配置認證信息) .formLogin() .usernameParameter("username") .passwordParameter("password") .loginPage("/login.jsp") .loginProcessingUrl("/login") .successForwardUrl("/index.jsp") .failureForwardUrl("/failer.jsp") .permitAll() // 登錄后,無論成功與否,都需要放行接下來的資源 .and() // 配置退出登錄信息 .logout() .logoutUrl("/logout") .logoutSuccessUrl("/login.jsp") .invalidateHttpSession(true) // 是否清空session .permitAll() .and() // csrf配置(csrf功能默認開啟,如果不需要關閉,下面都不用配置了) .csrf() .disable(); // 關閉csrf防護 } }
13.編寫啟動類
@SpringBootApplication @MapperScan("com.fgy.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
14.使用maven命令運行

