Spring Boot下载地址:https://start.spring.io/
【0】pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 热部署,可去掉 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 认证/授权 -->
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
【1】BrowerSecurityConfig.java
/**
* Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上
* 加@EnableGlobalMethodSecurity注解, 来判断用户对某个控制层的方法是否具有访问权限。
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private MyUserDetailsService myUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin() // 定义当需要用户登录时候,转到的登录页面。
//.loginPage("/login.html") // 登录页(默认Security会提供一个登录页)
//.usernameParameter("username")
//.passwordParameter("password")
//.failureForwardUrl("/login?error")
.and() //and()返回了HttpSecurity本身
.logout()
//.logoutUrl("/logout")
//.logoutSuccessUrl("/index")
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(new MyAccessDeniedHandler()) //访问拒绝处理
//.accessDeniedPage("/403")
//.accessDeniedHandler()
.and()
.authorizeRequests() //配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
.antMatchers("/**/common/*", "/index.html").permitAll() //允许所有用户访问"**/common/*"和"/index.html"
.anyRequest().authenticated(); // 其他地址的访问均需验证权限
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
【2】MyAccessDeniedHandler.java
@Service
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
if (HTTPUtils.isAjaxRequest(request)) {//AJAX请求,使用response发送403
response.sendError(403);
/*response.setHeader("noAuthentication", "true");
ResultWithObject resultWithObject = new ResultWithObject(CC.NEGATIVE_1, CC.RESULT_MESSAGE_TEXT_DEFAULT);
resultWithObject.setMsg("您无权限访问该请求!");
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JasonUtils.Object2String(resultWithObject));
writer.close();
response.flushBuffer(); */
} else if (!response.isCommitted()) {//非AJAX请求,跳转系统默认的403错误界面,在web.xml中配置
response.sendRedirect("/403"); //相当于 .accessDeniedPage("/403")
}
}
}
【3】MyUserDetailsService.java
@Service
public class MyUserDetailsService implements UserDetailsService{
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("用户的用户名: {}", username);
String password = passwordEncoder.encode("123456");
logger.info("password: {}", password);
/**
* 参数分别是:用户名,密码,用户权限(此处将其设置为:aaa)
*/
User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("aaa"));
return user;
}
/*@Autowired
private UserService userService;
*//**
* 授权的时候是对角色授权,而认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
*//*
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = userService.getUserByName(username);
if (null == sysUser) {
throw new UsernameNotFoundException(username);
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (SysRole role : sysUser.getRoleList()) {
for (SysPermission permission : role.getPermissionList()) {
authorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
}
return new User(sysUser.getUsername(), sysUser.getPassword(), authorities);
}*/
}
【4】HTTPUtils.java
public class HTTPUtils {
/**
* 判断是否为ajax请求
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
if (request.getHeader("accept").indexOf("application/json") > -1
|| ( request.getHeader("X-Requested-With") != null
&& request.getHeader("X-Requested-With").equals("XMLHttpRequest") ) ) {
return true;
}
return false;
}
}
【5】HelloController.java
@Controller
public class HelloController {
@RequestMapping("/403")
public String to403(HashMap<String, Object> map) {
map.put("message", "欢迎进入403页面");
return "/403";
}
/**
*(1) @PreAuthorize("hasAuthority('ddd')"):具有ddd权限的才能访问;
*(2)另有 @PreAuthorize("hasRole('userRole')"):具有userRole权限才能访问
*/
@PreAuthorize("hasAuthority('ddd')")
@RequestMapping("/hello/d")
public String toHello(HashMap<String, Object> map) {
map.put("message", "欢迎进入hello页面");
return "/hello";
}
}
【6】HelloRestController.java
@RestController
@RequestMapping("/demo")
public class HelloRestController {
@RequestMapping("/common/a")
public String common() {
return "Hello World1";
}
@PreAuthorize("hasAuthority('aaa')")
@RequestMapping("/index/a")
public String indexA() {
return "Hello WorldA";
}
@PreAuthorize("hasAuthority('bbb')")
@RequestMapping("/index/b")
public String indexB() {
return "Hello WorldC";
}
}
【7】
application.properties
#thymeleaf配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache=false
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
注意点:
(1)要使用 @PreAuthorize 注解,必须为继承WebSecurityConfigurerAdapter 的类添加以下注解:EnableGlobalMethodSecurity(prePostEnabled = true)
(2)@Controller 与 @RestController:
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。
RestController 注解源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
(3)@EnableGlobalMethodSecurity
1)@EnableGlobalMethodSecurity(securedEnabled=true) 开启@Secured 注解过滤权限;
2)@EnableGlobalMethodSecurity(jsr250Enabled=true) 开启@RolesAllowed 注解过滤权限;
3)@EnableGlobalMethodSecurity(prePostEnabled=true) 使用表达式时间方法级别的安全性,有4个注解可用。
(4)prePostEnabled注解
@PreAuthorize:
在方法执行之前执行,而且这里可以调用方法的参数,也可以得到参数值,这是利用JAVA8的参数名反射特性,如果没用JAVA8,那么也可以利用Spring Security的@P标注参数,或者Spring Data的@Param标注参数。
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){ }
这里表示在 changePassword 方法执行之前,判断方法参数userId 的值是否等于principal中保存的当前用户的 userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该方法。
@PostAuthorize:
在方法执行之后执行,但是如果表达式计算结果为false,将抛出一个安全性异常。EL变量 returnObject表示返回的对象。
@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");
@PreFilter:
在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改,EL变量filterObject表示参数,如有多个参数,使用filterTarget注解参数。只有方法参数是集合或数组才行。(很少会用到,与分页技术不兼容)
@PostFilter
PostFilter 允许方法调用, 但必须按照表达式来过滤方法的结果